Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> */ final class CheckGlobalThis implements Callback { static final DiagnosticType GLOBAL_THIS = DiagnosticType.warning( "JSC_USED_GLOBAL_THIS", "dangerous use of the global 'this' object"); private final AbstractCompiler compiler; private final CheckLevel level; /** * If {@code assignLhsChild != null}, then the node being traversed is * a descendant of the first child of an ASSIGN node. assignLhsChild's * parent is this ASSIGN node. */ private Node assignLhsChild = null; CheckGlobalThis(AbstractCompiler compiler, CheckLevel level) { this.compiler = compiler; this.level = level; // Only traverse the right side if it's not an assignment to a prototype // property or subproperty. if (lhs.getType() == Token.GETPROP) { if (lhs.getLastChild().getString().equals("prototype")) { return false; } String leftName = lhs.getQualifiedName(); if (leftName != null && leftName.contains(".prototype.")) { return false; } } JSDocInfo jsDoc = n.getJSDocInfo(); Node parent = n.getParent(); if (jsDoc == null) { int parentType = parent.getType(); if (parentType == Token.NAME || parentType == Token.ASSIGN) { jsDoc = parent.getJSDocInfo(); if (jsDoc == null && parentType == Token.NAME) { Node gramps = parent.getParent(); if (gramps.getType() == Token.VAR) { jsDoc = gramps.getJSDocInfo(); } } } } return jsDoc; } }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> endLine(); } @Override void endCaseBody() { super.endCaseBody(); indent--; endStatement(); } @Override void appendOp(String op, boolean binOp) { if (binOp) { if (getLastChar() != ' ') { append(" "); } append(op); append(" "); } else { append(op); } } /** * If the body of a for loop or the then clause of an if statement has * a single statement, should it be wrapped in a block? * {@inheritDoc} */ @Override boolean shouldPreserveExtraBlocks() { // When pretty-printing, always place the statement in its own block // so it is printed on a separate line. This allows breakpoints to be // placed on the statement. return true; } /** * @return The TRY node for the specified CATCH node. */ private Node getTryForCatch(Node n) { return n.getParent().getParent(); } /** * @return Whether the a line break should be added after the specified * BLOCK. */ @Override boolean breakAfterBlockFor(Node n, boolean isStatementContext) { Preconditions.checkState(n.getType() == Token.BLOCK); Node parent = n.getParent(); if (parent != null) { int type = parent.getType(); switch (type) { case Token.DO: // Don't break before 'while' in DO-WHILE statements. return false; case Token.FUNCTION: // FUNCTIONs are handled separately, don't break here. return false; case Token.TRY: // Don't break before catch return n != parent.getFirstChild(); case Token.CATCH: // Don't break before finally return !NodeUtil.hasFinally(getTryForCatch(parent)); case Token.IF: // Don't break before else return n == parent.getLastChild(); } } return true; } } static class CompactCodePrinter extends MappedCodePrinter implements HasGetCode { // The CompactCodePrinter tries to emit just enough newlines to stop there // being lines longer than the threshold. Since the output is going to be // gz

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>(condition, blindScope, NE); } else { return caseEquality(condition, blindScope, EQ); } case Token.SHEQ: if (outcome) { return caseEquality(condition, blindScope, SHEQ); } else { return caseEquality(condition, blindScope, SHNE); } case Token.SHNE: if (outcome) { return caseEquality(condition, blindScope, SHNE); } else { return caseEquality(condition, blindScope, SHEQ); } case Token.NAME: case Token.GETPROP: return caseNameOrGetProp(condition, blindScope, outcome); case Token.ASSIGN: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild().getNext(), blindScope, outcome), outcome); case Token.NOT: return firstPreciserScopeKnowingConditionOutcome( condition.getFirstChild(), blindScope, !outcome); case Token.LE: case Token.LT: case Token.GE: case Token.GT: if (outcome) { return caseEquality(condition, blindScope, INEQ); } break; case Token.INSTANCEOF: return caseInstanceOf( condition.getFirstChild(), condition.getLastChild(), blindScope, outcome); case Token.IN: if (outcome && condition.getFirstChild().getType() == Token.STRING) { return caseIn(condition.getLastChild(), condition.getFirstChild().getString(), blindScope); } break; case Token.CASE: Node left = condition.getParent().getFirstChild(); // the switch condition Node right = condition.getFirstChild(); if (outcome) { return caseEquality(left, right, blindScope, SHEQ); } else { return caseEquality(left, right, blindScope, SHNE); } } return nextPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome); } private FlowScope caseEquality(Node condition, FlowScope blindScope, Function<TypePair, TypePair> merging) { return caseEquality(condition.getFirstChild(), condition.getLastChild(), blindScope, merging); } private FlowScope caseEquality

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>compiler)); } } /** * Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code. */ private void normalizeNodeTypes(Node n) { if (n.getType() == Token.EXPR_VOID) { n.setType(Token.EXPR_RESULT); reportChange(); } // Remove unused properties to minimize differences between ASTs // produced by the two parsers. if (n.getType() == Token.FUNCTION) { Preconditions.checkState(n.getProp(Node.FUNCTION_PROP) == null); } normalizeBlocks(n); for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { // This pass is run during the CompilerTestCase validation, so this // parent pointer check serves as a more general check. Preconditions.checkState(child.getParent() == n); normalizeNodeTypes(child); } } /** * Add blocks to IF, WHILE, DO, etc. */ private void normalizeBlocks(Node n) { if (NodeUtil.isControlStructure(n) && n.getType() != Token.LABEL && n.getType() != Token.SWITCH) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (NodeUtil.isControlStructureCodeBlock(n,c) && c.getType() != Token.BLOCK) { Node newBlock = new Node(Token.BLOCK, n.getLineno(), n.getCharno()); newBlock.copyInformationFrom(n); n.replaceChild(c, newBlock); if (c.getType() != Token.EMPTY) { newBlock.addChildrenToFront(c); } else { newBlock.setWasEmptyNode(true); } c = newBlock; reportChange(); } } } } /** * Normalize where annotations appear on the AST. Copies * around existing JSDoc annotations as well as internal annotations. */ static class PrepareAnnotations extends NodeTraversal.AbstractPostOrderCallback { private final CodingConvention convention; PrepareAnnotations(AbstractCompiler compiler) { this.convention = compiler.getCodingConvention(); } /** * * In the AST that R

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>hino gives us, it needs to make a distinction * between jsdoc on the object literal node and jsdoc on the object literal * value. For example, * <pre> * var x = { * / JSDOC / * a: 'b', * c: / JSDOC / 'd' * }; * </pre> * * But in few narrow cases (in particular, function literals), it's * a lot easier for us if the doc is attached to the value. */ public void visit(NodeTraversal t, Node n, Node parent) { int nType = n.getType(); switch (nType) { case Token.NAME: case Token.STRING: String nString = n.getString(); if (nType == Token.NAME && n.getParent().getType() == Token.CALL && "eval".equals(nString)) { n.putBooleanProp(Node.DIRECT_EVAL, true); } if (convention.isConstant(nString)) { n.putBooleanProp(Node.IS_CONSTANT_NAME, true); } break; case Token.FUNCTION: JSDocInfo fnInfo = n.getJSDocInfo(); if (fnInfo == null) { // Look for the info on other nodes. if (parent.getType() == Token.ASSIGN) { // on ASSIGNs fnInfo = parent.getJSDocInfo(); } else if (parent.getType() == Token.NAME) { // on var NAME = function() { ... }; fnInfo = parent.getParent().getJSDocInfo(); } } // Compute which function parameters are optional and // which are var_args. Node args = n.getFirstChild().getNext(); for (Node arg = args.getFirstChild(); arg != null; arg = arg.getNext()) { String argName = arg.getString(); JSTypeExpression typeExpr = fnInfo == null ? null : fnInfo.getParameterType(argName); if (convention.isOptionalParameter(arg) || typeExpr != null && typeExpr.isOptionalArg()) { arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true); } if (convention.isVarArgsParameter

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> variable is still declared, // but no longer assigned to a value, e.g., // DEF_FOO = 5; // becomes "5;" // We can't remove the ASSIGN/VAR when we're still visiting its // children, so we'll have to come back later to remove it. refInfo.name.removeRef(ref); lvalueToRemoveLater = valParent; } break; default: if (t.inGlobalScope()) { // Treat this as a reference to a define in the global scope. // After this point, the define must not be reassigned, // or it's an error. DefineInfo info = assignableDefines.get(fullName); if (info != null) { setDefineInfoNotAssignable(info, t); assignableDefines.remove(fullName); } } break; } } if (!t.inGlobalScope() && n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) { // warn about @define annotations in local scopes compiler.report( t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, "")); } if (lvalueToRemoveLater == n) { lvalueToRemoveLater = null; if (n.getType() == Token.ASSIGN) { Node last = n.getLastChild(); n.removeChild(last); parent.replaceChild(n, last); } else { Preconditions.checkState(n.getType() == Token.NAME); n.removeChild(n.getFirstChild()); } compiler.reportCodeChange(); } if (n.getType() == Token.CALL) { if (t.inGlobalScope()) { // If there's a function call in the global scope, // we just say it's unsafe and freeze all the defines. // // NOTE(nicksantos): We could be a lot smarter here. For example, // ReplaceOverriddenVars keeps a call graph of all functions and // which functions/variables that they reference, and tries // to statically determine which functions are "safe" and which // are not. But this would be overkill, expecially because // the intended use of defines is with config_files, where // all the

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> defines are at the top of the bundle. for (DefineInfo info : assignableDefines.values()) { setDefineInfoNotAssignable(info, t); } assignableDefines.clear(); } } updateAssignAllowedStack(n, false); } /** * Determines whether assignment to a define should be allowed * in the subtree of the given node, and if not, records that fact. * * @param n The node whose subtree we're about to enter or exit. * @param entering True if we're entering the subtree, false otherwise. */ private void updateAssignAllowedStack(Node n, boolean entering) { switch (n.getType()) { case Token.CASE: case Token.FOR: case Token.FUNCTION: case Token.HOOK: case Token.IF: case Token.SWITCH: case Token.WHILE: if (entering) { assignAllowed.push(0); } else { assignAllowed.remove(); } break; } } /** * Determines whether assignment to a define should be allowed * at the current point of the traversal. */ private boolean isAssignAllowed() { return assignAllowed.element() == 1; } /** * Tracks the given define. * * @param t The current traversal, for context. * @param name The full name for this define. * @param value The value assigned to the define. * @param valueParent The parent node of value. * @return Whether we should remove this assignment from the parse tree. */ private boolean processDefineAssignment(NodeTraversal t, String name, Node value, Node valueParent) { if (value == null || !NodeUtil.isValidDefineValue(value, allDefines.keySet())) { compiler.report( t.makeError(value, INVALID_DEFINE_INIT_ERROR, name)); } else if (!isAssignAllowed()) { compiler.report( t.makeError(valueParent, NON_GLOBAL_DEFINE_INIT_ERROR, name)); } else { DefineInfo info = allDefines.get(name); if (info == null) { // First declaration of this define. info = new DefineInfo(value, valueParent); allDefines.put(name, info);

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> visit all nodes but not traverse into function * bodies. */ public abstract static class AbstractShallowCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { // We do want to traverse the name of a named function, but we don't // want to traverse the arguments or body. return parent == null || parent.getType() != Token.FUNCTION || n == parent.getFirstChild(); } } /** * Abstract callback to visit all structure and statement nodes but doesn't * traverse into functions or expressions. */ public abstract static class AbstractShallowStatementCallback implements Callback { public final boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return parent == null || NodeUtil.isControlStructure(parent) || NodeUtil.isStatementBlock(parent); } } /** * Abstract callback to visit a pruned set of nodes. * */ public abstract static class AbstractNodeTypePruningCallback implements Callback { private final Set<Integer> nodeTypes; private final boolean include; /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes) { this(nodeTypes, true); } /** * Creates an abstract pruned callback. * @param nodeTypes the nodes to include/exclude in the traversal * @param include whether to include or exclude the nodes in the traversal */ public AbstractNodeTypePruningCallback(Set<Integer> nodeTypes, boolean include) { this.nodeTypes = nodeTypes; this.include = include; } public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) { return include == nodeTypes.contains(n.getType()); } } /** * Creates a node traversal using the specified callback interface. */ public NodeTraversal(AbstractCompiler compiler, Callback cb) { this(compiler, cb, new SyntacticScopeCreator(compiler)); } /** * Creates a node traversal using the specified callback interface * and the scope creator. */ public NodeTraversal(AbstractCompiler compiler, Callback cb, ScopeCreator scopeCreator) { this.callback = cb;

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>fallthrough") private void traverseBranch(Node n, Node parent) { int type = n.getType(); if (type == Token.SCRIPT) { sourceName = getSourceName(n); } curNode = n; if (!callback.shouldTraverse(this, n, parent)) return; switch (type) { case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block traverseBranch(n.getFirstChild(), n); traverseBranch(n.getFirstChild().getNext().getNext(), n); break; case Token.FUNCTION: traverseFunction(n, parent); break; default: for (Node child = n.getFirstChild(); child != null; ) { // child could be replaced, in which case our child node // would no longer point to the true next Node next = child.getNext(); traverseBranch(child, n); child = next; } break; } curNode = n; callback.visit(this, n, parent); } /** * Traverses a function. */ private void traverseFunction(Node n, Node parent) { Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getType() == Token.FUNCTION); final Node fnName = n.getFirstChild(); boolean isFunctionExpression = (parent != null) && NodeUtil.isFunctionExpression(n); if (!isFunctionExpression) { // Functions declarations are in the scope containing the declaration. traverseBranch(fnName, n); } curNode = n; pushScope(n); if (isFunctionExpression) { // Function expression names are only accessible within the function // scope. traverseBranch(fnName, n); } final Node args = fnName.getNext(); final Node body = args.getNext(); // Args traverseBranch(args, n); // Body Preconditions.checkState(body.getNext() == null && body.getType() == Token.BLOCK); traverseBranch(body, n); popScope(); } /** Examines the functions stack for

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>IN_EXTERNS_ERROR = DiagnosticType.warning( "JSC_NAME_REFERENCE_IN_EXTERNS", "accessing name {0} in externs has no effect"); static final DiagnosticType INVALID_FUNCTION_DECL = DiagnosticType.error("JSC_INVALID_FUNCTION_DECL", "Syntax error: function declaration must have a name"); private CompilerInput synthesizedExternsInput = null; private Node synthesizedExternsRoot = null; private final AbstractCompiler compiler; // Whether this is the post-processing sanity check. private final boolean sanityCheck; VarCheck(AbstractCompiler compiler) { this(compiler, false); } VarCheck(AbstractCompiler compiler, boolean sanityCheck) { this.compiler = compiler; this.sanityCheck = sanityCheck; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck()); NodeTraversal.traverseRoots( compiler, Lists.newArrayList(externs, root), this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() != Token.NAME) { return; } String varName = n.getString(); // Only a function can have an empty name. if (varName.isEmpty()) { Preconditions.checkState(NodeUtil.isFunction(parent)); // A function declaration with an empty name passes Rhino, // but is supposed to be a syntax error according to the spec. if (!NodeUtil.isFunctionExpression(parent)) { t.report(n, INVALID_FUNCTION_DECL); } return; } // Check that the var has been declared. Scope scope = t.getScope(); Scope.Var var = scope.getVar(varName); if (var == null) { if (NodeUtil.isFunctionExpression(parent)) { // e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the // current scope. } else { t.report(n, UNDEFINED_VAR_ERROR, varName); if (sanityCheck) { throw new IllegalStateException("Unexpected variable " + varName); } else { // Create a

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> new variable in a synthetic script. This will prevent // subsequent compiler passes from crashing. Node nameNode = Node.newString(Token.NAME, varName); getSynthesizedExternsRoot().addChildToBack( new Node(Token.VAR, nameNode)); scope.getGlobalScope().declare(varName, nameNode, null, getSynthesizedExternsInput()); } } return; } CompilerInput currInput = t.getInput(); CompilerInput varInput = var.input; if (currInput == varInput || currInput == null || varInput == null) { // The variable was defined in the same file. This is fine. return; } // Check module dependencies. JSModule currModule = currInput.getModule(); JSModule varModule = varInput.getModule(); JSModuleGraph moduleGraph = compiler.getModuleGraph(); if (varModule != currModule && varModule != null && currModule != null) { if (moduleGraph.dependsOn(currModule, varModule)) { // The module dependency was properly declared. } else { if (!sanityCheck && scope.isGlobal()) { if (moduleGraph.dependsOn(varModule, currModule)) { // The variable reference violates a declared module dependency. t.report(n, VIOLATED_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } else { // The variable reference is between two modules that have no // dependency relationship. This should probably be considered an // error, but just issue a warning for now. t.report(n, MISSING_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } else { t.report(n, STRICT_MODULE_DEP_ERROR, currModule.getName(), varModule.getName(), varName); } } } } /** * A check for name references in the externs inputs. These used to prevent * a variable from getting renamed, but no longer have any effect. */ private class NameRefInExternsCheck extends AbstractPostOrderCallback { public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.NAME) { switch

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> of the preciser scope to the next link. * If there is no next link, returns the blind scope. */ protected FlowScope nextPreciserScopeKnowingConditionOutcome(Node condition, FlowScope blindScope, boolean outcome) { return nextLink != null ? nextLink.getPreciserScopeKnowingConditionOutcome( condition, blindScope, outcome) : blindScope; } /** * Returns the type of a node in the given scope if the node corresponds to a * name whose type is capable of being refined. * @return The current type of the node if it can be refined, null otherwise. */ JSType getTypeIfRefinable(Node node, FlowScope scope) { switch (node.getType()) { case Token.NAME: StaticSlot<JSType> nameVar = scope.getSlot(node.getString()); if (nameVar != null) { JSType nameVarType = nameVar.getType(); if (nameVarType == null) { nameVarType = node.getJSType(); } return nameVarType; } return null; case Token.GETPROP: String qualifiedName = node.getQualifiedName(); if (qualifiedName == null) { return null; } StaticSlot<JSType> propVar = scope.getSlot(qualifiedName); JSType propVarType = null; if (propVar != null) { propVarType = propVar.getType(); } if (propVarType == null) { propVarType = node.getJSType(); } if (propVarType == null) { propVarType = getNativeType(UNKNOWN_TYPE); } return propVarType; } return null; } /** * Declares a refined type in {@code scope} for the name represented by * {@code node}. It must be possible to refine the type of the given node in * the given scope, as determined by {@link #getTypeIfRefinable}. */ protected void declareNameInScope(FlowScope scope, Node node, JSType type) { switch (node.getType()) { case Token.NAME: scope.inferSlotType(node.getString(), type); break; case Token.GETPROP: String

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>Root) { Preconditions.checkState(scopeCreator == null); Preconditions.checkState(topScope == null); Preconditions.checkState(jsRoot.getParent() != null); Node externsAndJsRoot = jsRoot.getParent(); scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler)); topScope = scopeCreator.createScope(externsAndJsRoot, null); TypeInferencePass inference = new TypeInferencePass(compiler, reverseInterpreter, topScope, scopeCreator); inference.process(externsRoot, jsRoot); process(externsRoot, jsRoot); return topScope; } public void check(Node node, boolean externs) { Preconditions.checkNotNull(node); NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator); inExterns = externs; t.traverseWithScope(node, topScope); if (externs) { inferJSDocInfo.process(node, null); } else { inferJSDocInfo.process(null, node); } } public boolean shouldTraverse( NodeTraversal t, Node n, Node parent) { JSDocInfo info; switch (n.getType()) { case Token.SCRIPT: case Token.VAR: // @notypecheck info = n.getJSDocInfo(); if (info != null && info.isNoTypeCheck()) { return false; } break; case Token.FUNCTION: // @notypecheck info = n.getJSDocInfo(); info = (info == null) ? parent.getJSDocInfo() : info; if (info != null && info.isNoTypeCheck()) { return false; } // normal type checking final TypeCheck outerThis = this; final Scope outerScope = t.getScope(); final FunctionType functionType = (FunctionType) n.getJSType(); final String functionPrivateName = n.getFirstChild().getString(); if (functionPrivateName != null && functionPrivateName.length() > 0 && outerScope.isDeclared(functionPrivateName, false) && // Ideally, we would want to check whether the type in the scope // differs from the type being defined, but then the extern // re

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>declarations of built-in types generates spurious warnings. !(outerScope.getVar( functionPrivateName).getType() instanceof FunctionType)) { t.report(n, FUNCTION_MASKS_VARIABLE, functionPrivateName); } // TODO(user): Only traverse the function's body. The function's // name and arguments are traversed by the scope creator, and ideally // should not be traversed by the type checker. break; } return true; } /** * This is the meat of the type checking. It is basically one big switch, * with each case representing one type of parse tree node. The individual * cases are usually pretty straightforward. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. * @param parent The parent of the node n. */ public void visit(NodeTraversal t, Node n, Node parent) { JSType childType; JSType leftType, rightType; Node left, right; // To be explicitly set to false if the node is not typeable. boolean typeable = true; switch (n.getType()) { case Token.NAME: typeable = visitName(t, n, parent); break; case Token.LP: // If this is under a FUNCTION node, it is a parameter list and can be // ignored here. if (parent.getType() != Token.FUNCTION) { ensureTyped(t, n, getJSType(n.getFirstChild())); } else { typeable = false; } break; case Token.COMMA: ensureTyped(t, n, getJSType(n.getLastChild())); break; case Token.TRUE: case Token.FALSE: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.THIS: ensureTyped(t, n, t.getScope().getTypeOfThis()); break; case Token.REF_SPECIAL: ensureTyped(t, n); break; case Token.GET_REF: ensureTyped(t, n, getJSType(n.getFirstChild())); break; case Token.NULL: ensure

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>Typed(t, n, NULL_TYPE); break; case Token.NUMBER: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, NUMBER_TYPE); } else { typeable = false; } break; case Token.ARRAYLIT: ensureTyped(t, n, ARRAY_TYPE); break; case Token.STRING: if (n.getParent().getType() != Token.OBJECTLIT) { ensureTyped(t, n, STRING_TYPE); } else { typeable = false; } break; case Token.REGEXP: ensureTyped(t, n, REGEXP_TYPE); break; case Token.GETPROP: visitGetProp(t, n, parent); typeable = !(parent.getType() == Token.ASSIGN && parent.getFirstChild() == n); break; case Token.GETELEM: visitGetElem(t, n); // The type of GETELEM is always unknown, so no point counting that. // If that unknown leaks elsewhere (say by an assignment to another // variable), then it will be counted. typeable = false; break; case Token.VAR: visitVar(t, n); typeable = false; break; case Token.NEW: visitNew(t, n); typeable = true; break; case Token.CALL: visitCall(t, n); typeable = !NodeUtil.isExpressionNode(parent); break; case Token.RETURN: visitReturn(t, n); typeable = false; break; case Token.DEC: case Token.INC: left = n.getFirstChild(); validator.expectNumber( t, left, getJSType(left), "increment/decrement"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.NOT: ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.VOID: ensureTyped(t, n, VOID_TYPE); break; case Token.TYPEOF: ensureTyped(t, n, STRING_TYPE); break; case Token.BITNOT: childType = getJSType(n.getFirstChild());

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> if (!childType.matchesInt32Context()) { t.report(n, BIT_OPERATION, NodeUtil.opToStr(n.getType()), childType.toString()); } ensureTyped(t, n, NUMBER_TYPE); break; case Token.POS: case Token.NEG: left = n.getFirstChild(); validator.expectNumber(t, left, getJSType(left), "sign operator"); ensureTyped(t, n, NUMBER_TYPE); break; case Token.EQ: case Token.NE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); TernaryValue result = leftTypeRestricted.testForEquality(rightTypeRestricted); if (result != TernaryValue.UNKNOWN) { if (n.getType() == Token.NE) { result = result.not(); } t.report(n, DETERMINISTIC_TEST, leftType.toString(), rightType.toString(), result.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.SHEQ: case Token.SHNE: { leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined(); JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined(); if (!leftTypeRestricted.canTestForShallowEqualityWith( rightTypeRestricted)) { t.report(n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(), rightType.toString()); } ensureTyped(t, n, BOOLEAN_TYPE); break; } case Token.LT: case Token.LE: case Token.GT: case Token.GE: leftType = getJSType(n.getFirstChild()); rightType = getJSType(n.getLastChild()); if (rightType.isNumber()) { validator.expectNumber( t, n, leftType, "left side of numeric comparison"); } else

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> case Token.ASSIGN_SUB: case Token.ASSIGN_ADD: case Token.ASSIGN_MUL: case Token.LSH: case Token.RSH: case Token.URSH: case Token.DIV: case Token.MOD: case Token.BITOR: case Token.BITXOR: case Token.BITAND: case Token.SUB: case Token.ADD: case Token.MUL: visitBinaryOperator(n.getType(), t, n); break; case Token.DELPROP: if (!isReference(n.getFirstChild())) { t.report(n, BAD_DELETE); } ensureTyped(t, n, BOOLEAN_TYPE); break; case Token.CASE: JSType switchType = getJSType(parent.getFirstChild()); JSType caseType = getJSType(n.getFirstChild()); validator.expectSwitchMatchesCase(t, n, switchType, caseType); typeable = false; break; case Token.WITH: { Node child = n.getFirstChild(); childType = getJSType(child); validator.expectObject( t, child, childType, "with requires an object"); typeable = false; break; } case Token.FUNCTION: visitFunction(t, n); break; // These nodes have no interesting type behavior. case Token.LABEL: case Token.LABEL_NAME: case Token.SWITCH: case Token.BREAK: case Token.CATCH: case Token.TRY: case Token.SCRIPT: case Token.EXPR_RESULT: case Token.BLOCK: case Token.EMPTY: case Token.DEFAULT: case Token.CONTINUE: case Token.DEBUGGER: case Token.THROW: typeable = false; break; // These nodes require data flow analysis. case Token.DO: case Token.FOR: case Token.IF: case Token.WHILE: typeable = false; break; // These nodes are typed during the type inference. case Token.AND: case Token.HOOK: case Token.OBJECTLIT: case Token.OR: if (n.getJSType() != null) { // If we didn't run type inference. ensureTyped(t

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> perform checks in addition to the ones below if (object.getType() == Token.GETPROP) { JSType jsType = getJSType(object.getFirstChild()); if (jsType.isInterface() && object.getLastChild().getString().equals("prototype")) { visitInterfaceGetprop(t, assign, object, property, lvalue, rvalue); } } // /** @type ... */object.name = ...; if (info != null && info.hasType()) { visitAnnotatedAssignGetprop(t, assign, info.getType().evaluate(t.getScope(), typeRegistry), object, property, rvalue); return; } // /** @enum ... */object.name = ...; if (info != null && info.hasEnumParameterType()) { checkEnumInitializer( t, rvalue, info.getEnumParameterType().evaluate( t.getScope(), typeRegistry)); return; } // object.prototype = ...; if (property.equals("prototype")) { if (objectJsType instanceof FunctionType) { FunctionType functionType = (FunctionType) objectJsType; if (functionType.isConstructor()) { JSType rvalueType = rvalue.getJSType(); validator.expectObject(t, rvalue, rvalueType, OVERRIDING_PROTOTYPE_WITH_NON_OBJECT); } } else { // TODO(user): might want to flag that } return; } // object.prototype.property = ...; if (object.getType() == Token.GETPROP) { Node object2 = object.getFirstChild(); String property2 = NodeUtil.getStringValue(object.getLastChild()); if ("prototype".equals(property2)) { JSType jsType = object2.getJSType(); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; if (functionType.isConstructor() || functionType.isInterface()) { checkDeclaredPropertyInheritance( t, assign, functionType, property, info, getJSType(rvalue)); } } else { // TODO(user): might want to flag that } return; } } // object.property = ...; ObjectType type = ObjectType.cast( object

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>JsType.restrictByNotNullOrUndefined()); if (type != null) { if (type.hasProperty(property) && !type.isPropertyTypeInferred(property) && !propertyIsImplicitCast(type, property)) { validator.expectCanAssignToPropertyOf( t, assign, getJSType(rvalue), type.getPropertyType(property), object, property); } return; } } else if (lvalue.getType() == Token.NAME) { // variable with inferred type case JSType rvalueType = getJSType(assign.getLastChild()); Var var = t.getScope().getVar(lvalue.getString()); if (var != null) { if (var.isTypeInferred()) { return; } } } // fall through case JSType leftType = getJSType(lvalue); Node rightChild = assign.getLastChild(); JSType rightType = getJSType(rightChild); if (validator.expectCanAssignTo( t, assign, rightType, leftType, "assignment")) { ensureTyped(t, assign, rightType); } else { ensureTyped(t, assign); } } /** * Returns true if any type in the chain has an implictCast annotation for * the given property. */ private boolean propertyIsImplicitCast(ObjectType type, String prop) { for (; type != null; type = type.getImplicitPrototype()) { JSDocInfo docInfo = type.getOwnPropertyJSDocInfo(prop); if (docInfo != null && docInfo.isImplicitCast()) { return true; } } return false; } /** * Given a constructor type and a property name, check that the property has * the JSDoc annotation @override iff the property is declared on a * superclass. Several checks regarding inheritance correctness are also * performed. */ private void checkDeclaredPropertyInheritance( NodeTraversal t, Node n, FunctionType ctorType, String propertyName, JSDocInfo info, JSType propertyType) { // TODO(user): We're not 100% confident that type-checking works, // so we return quietly if the unknown type is a superclass of this type. // Remove this check

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> // property compiler.report(t.makeError(n, reportMissingOverride, HIDDEN_SUPERCLASS_PROPERTY, propertyName, topInstanceType.toString())); } if (!declaredOverride) { // there's no @override to check return; } // @override is present and we have to check that it is ok if (superClassHasProperty) { // there is a superclass implementation JSType superClassPropType = superClass.getPrototype().getPropertyType(propertyName); if (!propertyType.canAssignTo(superClassPropType)) { compiler.report( t.makeError(n, HIDDEN_SUPERCLASS_PROPERTY_MISMATCH, propertyName, topInstanceType.toString(), superClassPropType.toString(), propertyType.toString())); } } else if (!foundInterfaceProperty) { // there is no superclass nor interface implementation compiler.report( t.makeError(n, UNKNOWN_OVERRIDE, propertyName, ctorType.getInstanceType().toString())); } } /** * Visits an ASSIGN node for cases such as * <pre> * interface.property2.property = ...; * </pre> */ private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object, String property, Node lvalue, Node rvalue) { JSType rvalueType = getJSType(rvalue); String abstractMethodName = compiler.getCodingConvention().getAbstractMethodName(); if (!rvalueType.isOrdinaryFunction() && !(rvalue.isQualifiedName() && rvalue.getQualifiedName().equals(abstractMethodName))) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_MEMBERS_ONLY, abstractMethodName)); } if (assign.getLastChild().getType() == Token.FUNCTION && !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) { compiler.report( t.makeError(object, INTERFACE_FUNCTION_NOT_EMPTY, abstractMethodName)); } } /** * Visits an ASSIGN node for cases such as * <pre> * object.property = ...; * </pre> * that have an {@code @type} annotation. */ private void visitAnnotatedAssignGetprop(NodeTraversal t, Node assign, JS

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> * Determines whether this node is testing for the existence of a property. * If true, we will not emit warnings about a missing property. * * @param getProp The GETPROP being tested. */ private boolean isPropertyTest(Node getProp) { Node parent = getProp.getParent(); switch (parent.getType()) { case Token.CALL: return parent.getFirstChild() != getProp && compiler.getCodingConvention().isPropertyTestFunction(parent); case Token.IF: case Token.WHILE: case Token.DO: case Token.FOR: return NodeUtil.getConditionExpression(parent) == getProp; case Token.INSTANCEOF: case Token.TYPEOF: return true; case Token.AND: case Token.HOOK: return parent.getFirstChild() == getProp; } return false; } /** * Visits a GETELEM node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitGetElem(NodeTraversal t, Node n) { Node left = n.getFirstChild(); Node right = n.getLastChild(); validator.expectIndexMatch(t, n, getJSType(left), getJSType(right)); ensureTyped(t, n); } /** * Visits a VAR node. * * @param t The node traversal object that supplies context, such as the * scope chain to use in name lookups as well as error reporting. * @param n The node being visited. */ private void visitVar(NodeTraversal t, Node n) { // TODO(nicksantos): Fix this so that the doc info always shows up // on the NAME node. We probably want to wait for the parser // merge to fix this. JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null; for (Node name : n.children()) { Node value = name.getFirstChild(); // A null var would indicate a bug in the scope creation logic. Var var = t.getScope().getVar(name.getString()); if

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> This is a misplaced return, but the real JS will fail to compile, // so let it go. if (function == null) { return; } JSType jsType = getJSType(function); if (jsType instanceof FunctionType) { FunctionType functionType = (FunctionType) jsType; JSType returnType = functionType.getReturnType(); // if no return type is specified, undefined must be returned // (it's a void function) if (returnType == null) { returnType = getNativeType(VOID_TYPE); } // fetching the returned value's type Node valueNode = n.getFirstChild(); JSType actualReturnType; if (valueNode == null) { actualReturnType = getNativeType(VOID_TYPE); valueNode = n; } else { actualReturnType = getJSType(valueNode); } // verifying validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType, "inconsistent return type"); } } /** * This function unifies the type checking involved in the core binary * operators and the corresponding assignment operators. The representation * used internally is such that common code can handle both kinds of * operators easily. * * @param op The operator. * @param t The traversal object, needed to report errors. * @param n The node being checked. */ private void visitBinaryOperator(int op, NodeTraversal t, Node n) { Node left = n.getFirstChild(); JSType leftType = getJSType(left); Node right = n.getLastChild(); JSType rightType = getJSType(right); switch (op) { case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.LSH: case Token.RSH: case Token.ASSIGN_URSH: case Token.URSH: if (!leftType.matchesInt32Context()) { t.report(left, BIT_OPERATION, NodeUtil.opToStr(n.getType()), leftType.toString()); } if (!rightType.matchesUint32Context()) { t.report(right, BIT_OPERATION, NodeUtil.opToStr(n.getType()), rightType.toString()); }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> must be assignable to the enum's primitive type validator.expectCanAssignTo(t, value, getJSType(value), primitiveType, "element type must match enum's type"); // advancing twice value = value.getNext(); value = (value == null) ? null : value.getNext(); } } else if (value.getJSType() instanceof EnumType) { // TODO(user): Remove the instanceof check in favor // of a type.isEnumType() predicate. Currently, not all enum types are // implemented by the EnumClass, e.g. the unknown type and the any // type. The types need to be defined by interfaces such that an // implementation can implement multiple types interface. EnumType valueEnumType = (EnumType) value.getJSType(); JSType valueEnumPrimitiveType = valueEnumType.getElementsType().getPrimitiveType(); validator.expectCanAssignTo(t, value, valueEnumPrimitiveType, primitiveType, "incompatible enum element types"); } else { // The error condition is handled in TypedScopeCreator. } } /** * This predicate is used to determine if the node represents an expression * that is a Reference according to JavaScript definitions. * * @param n The node being checked. * @return true if the sub-tree n is a reference, false otherwise. */ private static boolean isReference(Node n) { switch (n.getType()) { case Token.GETELEM: case Token.GETPROP: case Token.NAME: return true; default: return false; } } /** * This method gets the JSType from the Node argument and verifies that it is * present. */ private JSType getJSType(Node n) { JSType jsType = n.getJSType(); if (jsType == null) { // TODO(nicksantos): This branch indicates a compiler bug, not worthy of // halting the compilation but we should log this and analyze to track // down why it happens. This is not critical and will be resolved over // time as the type checker is extended. return getNativeType(UNKNOWN_TYPE); } else { return jsType; } } /** * Gets the type of the node or

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Node; /** * Look for references to the global RegExp object that would cause * regular expressions to be unoptimizable. * * @author johnlenz@google.com (John Lenz) */ class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass { static final DiagnosticType REGEXP_REFERENCE = DiagnosticType.warning("JSC_REGEXP_REFERENCE", "References to the global RegExp object prevents " + "optimization of regular expressions."); private final AbstractCompiler compiler; private boolean globalRegExpPropertiesUsed = false; public boolean isGlobalRegExpPropertiesUsed() { return globalRegExpPropertiesUsed; } public CheckRegExp(AbstractCompiler compiler) { this.compiler = compiler; } @Override public void process(Node externs, Node root) { NodeTraversal.traverse(compiler, root, this); } @Override public void visit(NodeTraversal t, Node n, Node parent) { if (NodeUtil.isReferenceName(n)) { String name = n.getString(); if (name.equals("RegExp") && t.getScope().getVar(name) == null) { int parentType = parent.getType(); boolean first = (n == parent.getFirstChild()); if (!((parentType == Token.NEW && first) || (parentType == Token.CALL && first) || (parentType == Token.INSTANCEOF && !

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>OverviewInfo.setLicense(irNode.getJSDocInfo().getLicense()); } irNode.setJSDocInfo(fileOverviewInfo); } } return irNode; } private Node transformBlock(AstNode node) { Node irNode = transform(node); if (irNode.getType() != Token.BLOCK) { if (irNode.getType() == Token.EMPTY) { irNode.setType(Token.BLOCK); irNode.setWasEmptyNode(true); } else { Node newBlock = new Node(Token.BLOCK, irNode, irNode.getLineno(), irNode.getCharno()); irNode = newBlock; } } return irNode; } private Node transform(AstNode node) { String jsDoc = node.getJsDoc(); NodeWithJsDoc nodeWithJsDoc = null; if (jsDoc != null) { nodeWithJsDoc = new NodeWithJsDoc(); nodesWithJsDoc.put(jsDoc, nodeWithJsDoc); } Node irNode = justTransform(node); if (nodeWithJsDoc != null) { nodeWithJsDoc.node = irNode; } // If we have a named function, set the position to that of the name. if (irNode.getType() == Token.FUNCTION && irNode.getFirstChild().getLineno() != -1) { irNode.setLineno(irNode.getFirstChild().getLineno()); irNode.setCharno(irNode.getFirstChild().getCharno()); } else { if (irNode.getLineno() == -1) { // If we didn't already set the line, then set it now. This avoids // cases like ParenthesizedExpression where we just return a previous // node, but don't want the new node to get its parent's line number. int lineno = node.getLineno(); irNode.setLineno(lineno); int charno = position2charno(node.getAbsolutePosition()); irNode.setCharno(charno); } } return irNode; } /** * Creates a JsDocInfoParser and parses the JsDoc string. * * Used both for handling individual

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>Index - 1; } } private Node justTransform(AstNode node) { return transformDispatcher.process(node); } private class TransformDispatcher extends TypeSafeDispatcher<Node> { private Node processGeneric( com.google.javascript.jscomp.mozilla.rhino.Node n) { Node node = new Node(transformTokenType(n.getType())); for (com.google.javascript.jscomp.mozilla.rhino.Node child : n) { node.addChildToBack(transform((AstNode)child)); } return node; } /** * Transforms the given node and then sets its type to Token.STRING if it * was Token.NAME. If its type was already Token.STRING, then quotes it. * Used for properties, as the old AST uses String tokens, while the new one * uses Name tokens for unquoted strings. For example, in * var o = {'a' : 1, b: 2}; * the string 'a' is quoted, while the name b is turned into a string, but * unquoted. */ private Node transformAsString(AstNode n) { Node ret = transform(n); if (ret.getType() == Token.STRING) { ret.putBooleanProp(Node.QUOTED_PROP, true); } else if (ret.getType() == Token.NAME) { ret.setType(Token.STRING); } return ret; } @Override Node processArrayLiteral(ArrayLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = new Node(Token.ARRAYLIT); int skipCount = 0; for (AstNode child : literalNode.getElements()) { Node c = transform(child); if (c.getType() == Token.EMPTY) { skipCount++; } node.addChildToBack(c); } if (skipCount > 0) { int[] skipIndexes = new int[skipCount]; int i = 0; int j = 0; for (Node child : node.children()) { if (child.getType() == Token.EMPTY) { node.removeChild(child); skipIndexes[j]

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> = i; j++; } i++; } node.putProp(Node.SKIP_INDEXES_PROP, skipIndexes); } return node; } @Override Node processAssignment(Assignment assignmentNode) { return processInfixExpression(assignmentNode); } @Override Node processAstRoot(AstRoot rootNode) { Node node = new Node(Token.SCRIPT); for (com.google.javascript.jscomp.mozilla.rhino.Node child : rootNode) { node.addChildToBack(transform((AstNode)child)); } parseDirectives(node); return node; } /** * Parse the directives, encode them in the AST, and remove their nodes. * * For information on ES5 directives, see section 14.1 of * Ecma-262, Edition 5. * * It would be nice if Rhino would eventually take care of this for * us, but right now their directive-processing is a one-off. */ private void parseDirectives(Node node) { // Remove all the directives, and encode them in the AST. Set<String> directives = null; while (isDirective(node.getFirstChild())) { String directive = node.removeFirstChild().getFirstChild().getString(); if (directives == null) { directives = Sets.newHashSet(directive); } else { directives.add(directive); } } if (directives != null) { node.setDirectives(directives); } } private boolean isDirective(Node n) { if (n == null) return false; int nType = n.getType(); return (nType == Token.EXPR_RESULT || nType == Token.EXPR_VOID) && n.getFirstChild().getType() == Token.STRING && ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString()); } @Override Node processBlock(Block blockNode) { return processGeneric(blockNode); } @Override Node processBreakStatement(BreakStatement statementNode) { Node node = new Node(Token.BREAK); if (statementNode.getBreakLabel() != null) { Node labelName = transform(statementNode.getBreakLabel()); // Change the NAME

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> Add the offset // to the operator to get the correct character number. n.setCharno(position2charno(exprNode.getAbsolutePosition() + exprNode.getOperatorPosition())); return n; } @Override Node processKeywordLiteral(KeywordLiteral literalNode) { return new Node(transformTokenType(literalNode.getType())); } @Override Node processLabel(Label labelNode) { return Node.newString(Token.LABEL_NAME, labelNode.getName()); } @Override Node processLabeledStatement(LabeledStatement statementNode) { Node node = new Node(Token.LABEL); Node prev = null; Node cur = node; for (Label label : statementNode.getLabels()) { if (prev != null) { prev.addChildToBack(cur); } cur.addChildToBack(transform(label)); cur.setLineno(label.getLineno()); int clauseAbsolutePosition = position2charno(label.getAbsolutePosition()); cur.setCharno(clauseAbsolutePosition); prev = cur; cur = new Node(Token.LABEL); } prev.addChildToBack(transform(statementNode.getStatement())); return node; } @Override Node processName(Name nameNode) { return Node.newString(Token.NAME, nameNode.getIdentifier()); } @Override Node processNewExpression(NewExpression exprNode) { return processFunctionCall(exprNode); } @Override Node processNumberLiteral(NumberLiteral literalNode) { Node newNode = Node.newNumber(literalNode.getNumber()); return newNode; } @Override Node processObjectLiteral(ObjectLiteral literalNode) { if (literalNode.isDestructuring()) { reportDestructuringAssign(literalNode); } Node node = new Node(Token.OBJECTLIT); for (ObjectProperty el : literalNode.getElements()) { if (el.isGetter()) { reportGetter(el); } else if (el.isSetter()) { reportSetter(el); } else { node.addChildToBack(transformAsString(el.getLeft())); node.addChildToBack(transform(el.getRight())); } } return node; } @Override

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> Node processObjectProperty(ObjectProperty propertyNode) { return processInfixExpression(propertyNode); } @Override Node processParenthesizedExpression(ParenthesizedExpression exprNode) { Node node = transform(exprNode.getExpression()); node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE); return node; } @Override Node processPropertyGet(PropertyGet getNode) { return new Node( Token.GETPROP, transform(getNode.getTarget()), transformAsString(getNode.getProperty())); } @Override Node processRegExpLiteral(RegExpLiteral literalNode) { Node literalStringNode = Node.newString(literalNode.getValue()); // assume it's on the same line. literalStringNode.setLineno(literalNode.getLineno()); Node node = new Node(Token.REGEXP, literalStringNode); String flags = literalNode.getFlags(); if (flags != null && !flags.isEmpty()) { Node flagsNode = Node.newString(flags); // Assume the flags are on the same line as the literal node. flagsNode.setLineno(literalNode.getLineno()); node.addChildToBack(flagsNode); } return node; } @Override Node processReturnStatement(ReturnStatement statementNode) { Node node = new Node(Token.RETURN); if (statementNode.getReturnValue() != null) { node.addChildToBack(transform(statementNode.getReturnValue())); } return node; } @Override Node processScope(Scope scopeNode) { return processGeneric(scopeNode); } @Override Node processStringLiteral(StringLiteral literalNode) { Node n = Node.newString(literalNode.getValue()); return n; } @Override Node processSwitchCase(SwitchCase caseNode) { Node node; if (caseNode.isDefault()) { node = new Node(Token.DEFAULT); } else { AstNode expr = caseNode.getExpression(); node = new Node(Token.CASE, transform(expr)); } Node block = new Node(Token.BLOCK); block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true); block.setLineno(caseNode.getLineno()); block.

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>setCharno(position2charno(caseNode.getAbsolutePosition())); if (caseNode.getStatements() != null) { for (AstNode child : caseNode.getStatements()) { block.addChildToBack(transform(child)); } } node.addChildToBack(block); return node; } @Override Node processSwitchStatement(SwitchStatement statementNode) { Node node = new Node(Token.SWITCH, transform(statementNode.getExpression())); for (AstNode child : statementNode.getCases()) { node.addChildToBack(transform(child)); } return node; } @Override Node processThrowStatement(ThrowStatement statementNode) { return new Node(Token.THROW, transform(statementNode.getExpression())); } @Override Node processTryStatement(TryStatement statementNode) { Node node = new Node(Token.TRY, transformBlock(statementNode.getTryBlock())); Node block = new Node(Token.BLOCK); boolean lineSet = false; for (CatchClause cc : statementNode.getCatchClauses()) { // Mark the enclosing block at the same line as the first catch // clause. if (lineSet == false) { block.setLineno(cc.getLineno()); lineSet = true; } block.addChildToBack(transform(cc)); } node.addChildToBack(block); AstNode finallyBlock = statementNode.getFinallyBlock(); if (finallyBlock != null) { node.addChildToBack(transformBlock(finallyBlock)); } // If we didn't set the line on the catch clause, then // we've got an empty catch clause. Set its line to be the same // as the finally block (to match Old Rhino's behavior.) if ((lineSet == false) && (finallyBlock != null)) { block.setLineno(finallyBlock.getLineno()); } return node; } @Override Node processUnaryExpression(UnaryExpression exprNode) { Node node = new Node(transformTokenType(exprNode.getType()), transform(exprNode.getOperand())); if (exprNode.isPostfix()) { node.putBooleanProp(Node.INCRDECR_PROP, true);

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.rhino.Token.NAME: return Token.NAME; case com.google.javascript.jscomp.mozilla.rhino.Token.NUMBER: return Token.NUMBER; case com.google.javascript.jscomp.mozilla.rhino.Token.STRING: return Token.STRING; case com.google.javascript.jscomp.mozilla.rhino.Token.NULL: return Token.NULL; case com.google.javascript.jscomp.mozilla.rhino.Token.THIS: return Token.THIS; case com.google.javascript.jscomp.mozilla.rhino.Token.FALSE: return Token.FALSE; case com.google.javascript.jscomp.mozilla.rhino.Token.TRUE: return Token.TRUE; case com.google.javascript.jscomp.mozilla.rhino.Token.SHEQ: return Token.SHEQ; case com.google.javascript.jscomp.mozilla.rhino.Token.SHNE: return Token.SHNE; case com.google.javascript.jscomp.mozilla.rhino.Token.REGEXP: return Token.REGEXP; case com.google.javascript.jscomp.mozilla.rhino.Token.BINDNAME: return Token.BINDNAME; case com.google.javascript.jscomp.mozilla.rhino.Token.THROW: return Token.THROW; case com.google.javascript.jscomp.mozilla.rhino.Token.RETHROW: return Token.RETHROW; case com.google.javascript.jscomp.mozilla.rhino.Token.IN: return Token.IN; case com.google.javascript.jscomp.mozilla.rhino.Token.INSTANCEOF: return Token.INSTANCEOF; case com.google.javascript.jscomp.mozilla.rhino.Token.LOCAL_LOAD: return Token.LOCAL_LOAD; case com.google.javascript.jscomp.mozilla.rhino.Token.GETVAR: return Token.GETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.SETVAR: return Token.SETVAR; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH_SCOPE: return Token.CATCH_SCOPE; case com

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>google.javascript.jscomp.mozilla.rhino.Token.SWITCH: return Token.SWITCH; case com.google.javascript.jscomp.mozilla.rhino.Token.CASE: return Token.CASE; case com.google.javascript.jscomp.mozilla.rhino.Token.DEFAULT: return Token.DEFAULT; case com.google.javascript.jscomp.mozilla.rhino.Token.WHILE: return Token.WHILE; case com.google.javascript.jscomp.mozilla.rhino.Token.DO: return Token.DO; case com.google.javascript.jscomp.mozilla.rhino.Token.FOR: return Token.FOR; case com.google.javascript.jscomp.mozilla.rhino.Token.BREAK: return Token.BREAK; case com.google.javascript.jscomp.mozilla.rhino.Token.CONTINUE: return Token.CONTINUE; case com.google.javascript.jscomp.mozilla.rhino.Token.VAR: return Token.VAR; case com.google.javascript.jscomp.mozilla.rhino.Token.WITH: return Token.WITH; case com.google.javascript.jscomp.mozilla.rhino.Token.CATCH: return Token.CATCH; case com.google.javascript.jscomp.mozilla.rhino.Token.FINALLY: return Token.FINALLY; case com.google.javascript.jscomp.mozilla.rhino.Token.VOID: return Token.VOID; case com.google.javascript.jscomp.mozilla.rhino.Token.RESERVED: return Token.RESERVED; case com.google.javascript.jscomp.mozilla.rhino.Token.EMPTY: return Token.EMPTY; case com.google.javascript.jscomp.mozilla.rhino.Token.BLOCK: return Token.BLOCK; case com.google.javascript.jscomp.mozilla.rhino.Token.LABEL: return Token.LABEL; case com.google.javascript.jscomp.mozilla.rhino.Token.TARGET: return Token.TARGET; case com.google.javascript.jscomp.mozilla.rhino.Token.LOOP: return Token.LOOP; case com.google.javascript.jscomp.mozilla.rhino

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.Token.EXPR_VOID: case com.google.javascript.jscomp.mozilla.rhino.Token.EXPR_RESULT: return Token.EXPR_RESULT; case com.google.javascript.jscomp.mozilla.rhino.Token.JSR: return Token.JSR; case com.google.javascript.jscomp.mozilla.rhino.Token.SCRIPT: return Token.SCRIPT; case com.google.javascript.jscomp.mozilla.rhino.Token.TYPEOFNAME: return Token.TYPEOFNAME; case com.google.javascript.jscomp.mozilla.rhino.Token.USE_STACK: return Token.USE_STACK; case com.google.javascript.jscomp.mozilla.rhino.Token.SETPROP_OP: return Token.SETPROP_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.SETELEM_OP: return Token.SETELEM_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.LOCAL_BLOCK: return Token.LOCAL_BLOCK; case com.google.javascript.jscomp.mozilla.rhino.Token.SET_REF_OP: return Token.SET_REF_OP; case com.google.javascript.jscomp.mozilla.rhino.Token.DOTDOT: return Token.DOTDOT; case com.google.javascript.jscomp.mozilla.rhino.Token.COLONCOLON: return Token.COLONCOLON; case com.google.javascript.jscomp.mozilla.rhino.Token.XML: return Token.XML; case com.google.javascript.jscomp.mozilla.rhino.Token.DOTQUERY: return Token.DOTQUERY; case com.google.javascript.jscomp.mozilla.rhino.Token.XMLATTR: return Token.XMLATTR; case com.google.javascript.jscomp.mozilla.rhino.Token.XMLEND: return Token.XMLEND; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_OBJECT: return Token.TO_OBJECT; case com.google.javascript.jscomp.mozilla.rhino.Token.TO_DOUBLE: return Token.TO_

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> return getPassConfig().getScopeCreator(); } @Override public Scope getTopScope() { return getPassConfig().getTopScope(); } @Override public ReverseAbstractInterpreter getReverseAbstractInterpreter() { if (abstractInterpreter == null) { ChainableReverseAbstractInterpreter interpreter = new SemanticReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()); if (options.closurePass) { interpreter = new ClosureReverseAbstractInterpreter( getCodingConvention(), getTypeRegistry()) .append(interpreter).getFirst(); } abstractInterpreter = interpreter; } return abstractInterpreter; } @Override TypeValidator getTypeValidator() { if (typeValidator == null) { typeValidator = new TypeValidator(this); } return typeValidator; } //------------------------------------------------------------------------ // Parsing //------------------------------------------------------------------------ /** * Parses the externs and main inputs. * * @return A synthetic root node whose two children are the externs root * and the main root */ Node parseInputs() { boolean devMode = options.devMode != DevMode.OFF; // If old roots exist (we are parsing a second time), detach each of the // individual file parse trees. if (externsRoot != null) { externsRoot.detachChildren(); } if (jsRoot != null) { jsRoot.detachChildren(); } // Parse main js sources. jsRoot = new Node(Token.BLOCK); jsRoot.setIsSyntheticBlock(true); if (options.tracer.isOn()) { tracker = new PerformanceTracker(jsRoot, options.tracer == TracerMode.ALL); addChangeHandler(tracker.getCodeChangeHandler()); } Tracer tracer = newTracer("parseInputs"); try { // Parse externs sources. externsRoot = new Node(Token.BLOCK); externsRoot.setIsSyntheticBlock(true); for (CompilerInput input : externs) { Node n = input.getAstRoot(this); if (hasErrors()) { return null; } externsRoot.addChildToBack(n); } // Check if the sources need to be re-ordered. if (options.manageClosureDependencies) { for (CompilerInput input : inputs) { input.setCompiler

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> sia = new SourceInformationAnnotator(input.getName()); NodeTraversal.traverse(this, n, sia); } jsRoot.addChildToBack(n); } externAndJsRoot = new Node(Token.BLOCK, externsRoot, jsRoot); externAndJsRoot.setIsSyntheticBlock(true); return externAndJsRoot; } finally { stopTracer(tracer, "parseInputs"); } } public Node parse(JSSourceFile file) { initCompilerOptionsIfTesting(); addToDebugLog("Parsing: " + file.getName()); return new JsAst(file).getAstRoot(this); } @Override Node parseSyntheticCode(String js) { CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [synthetic] ", js)); inputsByName.put(input.getName(), input); return input.getAstRoot(this); } void initCompilerOptionsIfTesting() { if (options == null) { // initialization for tests that don't initialize the compiler // by the normal mechanisms. initOptions(new CompilerOptions()); } } @Override Node parseSyntheticCode(String fileName, String js) { initCompilerOptionsIfTesting(); return parse(JSSourceFile.fromCode(fileName, js)); } Node parseTestCode(String js) { initCompilerOptionsIfTesting(); CompilerInput input = new CompilerInput( JSSourceFile.fromCode(" [testcode] ", js)); if (inputsByName == null) { inputsByName = Maps.newHashMap(); } inputsByName.put(input.getName(), input); return input.getAstRoot(this); } @Override ErrorReporter getDefaultErrorReporter() { return defaultErrorReporter; } //------------------------------------------------------------------------ // Convert back to source code //------------------------------------------------------------------------ /** * Converts the main parse tree back to js code. */ public String toSource() { return runInCompilerThread(new Callable<String>() { public String call() throws Exception { Tracer tracer = newTracer("toSource"); try { CodeBuilder cb = new CodeBuilder(); if (jsRoot != null) { int i = 0; for (Node scriptNode =

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>Inputs = inputs.size(); if (numInputs == 0) { return new String[0]; } String[] sources = new String[numInputs]; CodeBuilder cb = new CodeBuilder(); for (int i = 0; i < numInputs; i++) { Node scriptNode = inputs.get(i).getAstRoot(Compiler.this); if (scriptNode == null) { throw new IllegalArgumentException( "Bad module input: " + inputs.get(i).getName()); } cb.reset(); toSource(cb, i, scriptNode); sources[i] = cb.toString(); } return sources; } }); } /** * Writes out js code from a root node. If printing input delimiters, this * method will attach a comment to the start of the text indicating which * input the output derived from. If there were any preserve annotations * within the root's source, they will also be printed in a block comment * at the beginning of the output. */ public void toSource(final CodeBuilder cb, final int inputSeqNum, final Node root) { runInCompilerThread(new Callable<Void>() { public Void call() throws Exception { if (options.printInputDelimiter) { if ((cb.getLength() > 0) && !cb.endsWith("\n")) { cb.append("\n"); // Make sure that the label starts on a new line } Preconditions.checkState(root.getType() == Token.SCRIPT); String delimiter = options.inputDelimiter; String sourceName = (String)root.getProp(Node.SOURCENAME_PROP); Preconditions.checkState(sourceName != null); Preconditions.checkState(!sourceName.isEmpty()); delimiter = delimiter.replaceAll("%name%", sourceName) .replaceAll("%num%", String.valueOf(inputSeqNum)); cb.append(delimiter) .append("\n"); } if (root.getJSDocInfo() != null && root.getJSDocInfo().getLicense() != null) { cb.append("/*\n") .append(root.getJSDocInfo().getLicense()) .append("*/\n"); } // If there is a valid source map, then indicate to

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> { try { parse(compiler, sourceFile.getName(), sourceFile.getCode()); } catch (IOException e) { compiler.report( JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName())); } } private void parse(AbstractCompiler compiler, String sourceName, String sourceStr) { try { logger_.fine("Parsing: " + sourceName); root = ParserRunner.parse(sourceName, sourceStr, compiler.getParserConfig(), compiler.getDefaultErrorReporter(), logger_); } catch (IOException e) { compiler.report(JSError.make(AbstractCompiler.READ_ERROR, sourceName)); } if (root == null || compiler.hasHaltingErrors()) { // There was a parse error or IOException, so use a dummy block. root = new Node(Token.BLOCK); } else { compiler.prepareAst(root); } // Set the source name so that the compiler passes can track // the source file and module. root.putProp(Node.SOURCENAME_PROP, sourceName); } }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> AbstractCompiler compiler, RedeclarationHandler redeclarationHandler) { this.compiler = compiler; this.redeclarationHandler = redeclarationHandler; } public Scope createScope(Node n, Scope parent) { sourceName = null; if (parent == null) { scope = new Scope(n, compiler); } else { scope = new Scope(parent, n); } scanRoot(n, parent); sourceName = null; Scope returnedScope = scope; scope = null; return returnedScope; } private void scanRoot(Node n, Scope parent) { if (n.getType() == Token.FUNCTION) { sourceName = (String) n.getProp(Node.SOURCENAME_PROP); final Node fnNameNode = n.getFirstChild(); final Node args = fnNameNode.getNext(); final Node body = args.getNext(); // Bleed the function name into the scope, if it hasn't // been declared in the outer scope. String fnName = fnNameNode.getString(); if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) { declareVar(fnName, fnNameNode, n, null, null, n); } // Args: Declare function variables Preconditions.checkState(args.getType() == Token.LP); for (Node a = args.getFirstChild(); a != null; a = a.getNext()) { Preconditions.checkState(a.getType() == Token.NAME); declareVar(a.getString(), a, args, n, null, n); } // Body scanVars(body, n); } else { // It's the global block Preconditions.checkState(scope.getParent() == null); scanVars(n, null); } } /** * Scans and gather variables declarations under a Node */ private void scanVars(Node n, Node parent) { switch (n.getType()) { case Token.VAR: // Declare all variables. e.g. var x = 1, y, z; for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); Preconditions.checkState(child.getType() == Token.NAME); String name = child.getString

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>(); declareVar(name, child, n, parent, null, n); child = next; } return; case Token.FUNCTION: if (NodeUtil.isFunctionExpression(n)) { return; } String fnName = n.getFirstChild().getString(); if (fnName.isEmpty()) { // This is invalid, but allow it so the checks can catch it. return; } declareVar(fnName, n.getFirstChild(), n, parent, null, n); return; // should not examine function's children case Token.CATCH: Preconditions.checkState(n.getChildCount() == 3); Preconditions.checkState(n.getFirstChild().getType() == Token.NAME); // the first child is the catch var and the third child // is the code block final Node var = n.getFirstChild(); final Node block = var.getNext().getNext(); declareVar(var.getString(), var, n, parent, null, n); scanVars(block, n); return; // only one child to scan case Token.SCRIPT: sourceName = (String) n.getProp(Node.SOURCENAME_PROP); break; } // Variables can only occur in statement-level nodes, so // we only need to traverse children in a couple special cases. if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) { for (Node child = n.getFirstChild(); child != null;) { Node next = child.getNext(); scanVars(child, n); child = next; } } } /** * Interface for injectable duplicate handling. */ interface RedeclarationHandler { void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber); } /** * The default handler for duplicate declarations. */ private class DefaultRedeclarationHandler implements RedeclarationHandler { public void onRedeclaration( Scope s, String name, Node n, Node parent, Node gramps, Node nodeWithLineNumber) { // Don't allow multiple variables to be declared at the top level scope if (scope.isGlobal()) { Scope.Var origVar = scope.getVar(name);

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> Node origParent = origVar.getParentNode(); if (origParent.getType() == Token.CATCH && parent.getType() == Token.CATCH) { // Okay, both are 'catch(x)' variables. return; } boolean allowDupe = false; JSDocInfo info = n.getJSDocInfo(); if (info == null) { info = parent.getJSDocInfo(); } allowDupe = info != null && info.getSuppressions().contains("duplicate"); if (!allowDupe) { compiler.report( JSError.make(sourceName, nodeWithLineNumber, VAR_MULTIPLY_DECLARED_ERROR, name, (origVar.input != null ? origVar.input.getName() : "??"))); } } else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) { // Disallow shadowing "arguments" as we can't handle with our current // scope modeling. compiler.report( JSError.make(sourceName, nodeWithLineNumber, VAR_ARGUMENTS_SHADOWED_ERROR)); } } } /** * Declares a variable. * * @param name The variable name * @param n The node corresponding to the variable name (usually a NAME node) * @param parent The parent node of {@code n} * @param gramps The parent node of {@code parent} * @param declaredType The variable's type, according to JSDoc * @param nodeWithLineNumber The node to use to access the line number of * the variable declaration, if needed */ private void declareVar(String name, Node n, Node parent, Node gramps, JSType declaredType, Node nodeWithLineNumber) { if (scope.isDeclared(name, false) || (scope.isLocal() && name.equals(ARGUMENTS))) { redeclarationHandler.onRedeclaration( scope, name, n, parent, gramps, nodeWithLineNumber); } else { scope.declare(name, n, declaredType, compiler.getInput(sourceName)); } } }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.jstype.FunctionType; import com.google.javascript.rhino.jstype.JSType; import com.google.javascript.rhino.jstype.ObjectType; import java.nio.charset.Charset; import java.util.Set; /** * A code generator that outputs type annotations for functions and * constructors. * */ class TypedCodeGenerator extends CodeGenerator { TypedCodeGenerator(CodeConsumer consumer, Charset outputCharset) { super(consumer, outputCharset); } @Override void add(Node n, Context context) { Node parent = n.getParent(); if (parent != null && (parent.getType() == Token.BLOCK || parent.getType() == Token.SCRIPT)) { if (n.getType() == Token.FUNCTION) { add(getFunctionAnnotation(n)); } else if (n.getType() == Token.EXPR_RESULT && n.getFirstChild().getType() == Token.ASSIGN) { Node rhs = n.getFirstChild().getLastChild(); add(getTypeAnnotation(rhs)); } else if (n.getType() == Token.VAR && n.getFirstChild().getFirstChild() != null && n.getFirstChild().getFirstChild().getType() == Token.FUNCTION) { add(getFunctionAnnotation(n.getFirstChild().getFirstChild

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>())); } } super.add(n, context); } private String getTypeAnnotation(Node node) { JSType type = node.getJSType(); if (type instanceof FunctionType) { return getFunctionAnnotation(node); } else if (type != null && !type.isUnknownType() && !type.isEmptyType() && !type.isVoidType() && !type.isFunctionPrototypeType()) { return "/** @type {" + node.getJSType() + "} */\n"; } else { return ""; } } /** * @param fnNode A node for a function for which to generate a type annotation */ private String getFunctionAnnotation(Node fnNode) { Preconditions.checkState(fnNode.getType() == Token.FUNCTION); StringBuilder sb = new StringBuilder("/**\n"); JSType type = fnNode.getJSType(); if (type == null || type.isUnknownType()) { return ""; } FunctionType funType = (FunctionType) fnNode.getJSType(); // We need to use the child nodes of the function as the nodes for the // parameters of the function type do not have the real parameter names. // FUNCTION // NAME // LP // NAME param1 // NAME param2 if (fnNode != null) { Node paramNode = NodeUtil.getFnParameters(fnNode).getFirstChild(); // Param types for (Node n : funType.getParameters()) { // Bail out if the paramNode is not there. if (paramNode == null) { break; } sb.append(" * @param {" + n.getJSType() + "} "); sb.append(paramNode.getString()); sb.append("\n"); paramNode = paramNode.getNext(); } } // Return type JSType retType = funType.getReturnType(); if (retType != null && !retType.isUnknownType() && !retType.isEmptyType()) { sb.append(" * @return {" + retType + "}\n"); } // Constructor/interface if (funType.isConstructor() || funType.isInterface()) { FunctionType superConstructor = funType.getSuperClassConstructor(); if (super

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> } else if (NodeUtil.isFunctionDeclaration(n) || parent.getType() == Token.NAME) { return normalizeClassType(n.getJSType()); } return null; } /** * Normalize the type of a constructor, its instance, and its prototype * all down to the same type (the instance type). */ private JSType normalizeClassType(JSType type) { if (type == null || type.isUnknownType()) { return type; } else if (type.isConstructor()) { return ((FunctionType) type).getInstanceType(); } else if (type.isFunctionPrototypeType()) { FunctionType owner = ((FunctionPrototypeType) type).getOwnerFunction(); if (owner.isConstructor()) { return owner.getInstanceType(); } } return type; } public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { return true; } public void visit(NodeTraversal t, Node n, Node parent) { switch (n.getType()) { case Token.NAME: checkNameDeprecation(t, n, parent); checkNameVisibility(t, n, parent); break; case Token.GETPROP: checkPropertyDeprecation(t, n, parent); checkPropertyVisibility(t, n, parent); break; case Token.NEW: checkConstructorDeprecation(t, n, parent); break; } } /** * Checks the given NEW node to ensure that access restrictions are obeyed. */ private void checkConstructorDeprecation(NodeTraversal t, Node n, Node parent) { JSType type = n.getJSType(); if (type != null) { String deprecationInfo = getTypeDeprecationInfo(type); if (deprecationInfo != null && shouldEmitDeprecationWarning(t, n, parent)) { if (!deprecationInfo.isEmpty()) { compiler.report( t.makeError(n, DEPRECATED_CLASS_REASON, type.toString(), deprecationInfo)); } else { compiler.report( t.makeError(n, DEPRECATED_CLASS, type.toString())); } } } } /** * Checks the given NAME node to ensure that access restrictions are obeyed. */ private void checkNameDe

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> example, * new PrivateCtor_(); // not legal * PrivateCtor_.newInstance(); // legal * x instanceof PrivateCtor_ // legal * * This is a weird special case, because our visibility system is inherited * from Java, and JavaScript has no distinction between classes and * constructors like Java does. * * We may want to revisit this if we decide to make the restrictions tighter. */ private static boolean isValidPrivateConstructorAccess(Node parent) { return parent.getType() != Token.NEW; } /** * Determines whether a deprecation warning should be emitted. * @param t The current traversal. * @param n The node which we are checking. * @param parent The parent of the node which we are checking. */ private boolean shouldEmitDeprecationWarning( NodeTraversal t, Node n, Node parent) { // In the global scope, there are only two kinds of accesses that should // be flagged for warnings: // 1) Calls of deprecated functions and methods. // 2) Instantiations of deprecated classes. // For now, we just let everything else by. if (t.inGlobalScope()) { if (!((parent.getType() == Token.CALL && parent.getFirstChild() == n) || n.getType() == Token.NEW)) { return false; } } // We can always assign to a deprecated property, to keep it up to date. if (n.getType() == Token.GETPROP && n == parent.getFirstChild() && NodeUtil.isAssignmentOp(parent)) { return false; } return !canAccessDeprecatedTypes(t); } /** * Returns whether it's currently ok to access deprecated names and * properties. * * There are 3 exceptions when we're allowed to use a deprecated * type or property: * 1) When we're in a deprecated function. * 2) When we're in a deprecated class. * 3) When we're in a static method of a deprecated class. */ private boolean canAccessDeprecatedTypes(NodeTraversal t) { Node scopeRoot = t.getScopeRoot(); Node scopeRootParent = scopeRoot.getParent(); return // Case #1 (deprecatedDepth > 0) || // Case #

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> FunctionParamBuilder(JSTypeRegistry registry) { this.registry = registry; } /** * Add parameters of the given type to the end of the param list. * @return False if this is called after optional params are added. */ public boolean addRequiredParams(JSType ...types) { if (hasOptionalOrVarArgs()) { return false; } for (JSType type : types) { newParameter(type); } return true; } /** * Add optional parameters of the given type to the end of the param list. * @param types Types for each optional parameter. The builder will make them * undefineable. * @return False if this is called after var args are added. */ public boolean addOptionalParams(JSType ...types) { if (hasVarArgs()) { return false; } for (JSType type : types) { newParameter(registry.createOptionalType(type)).setOptionalArg(true); } return true; } /** * Add variable arguments to the end of the parameter list. * @return False if this is called after var args are added. */ public boolean addVarArgs(JSType type) { if (hasVarArgs()) { return false; } // There are two types of variable argument functions: // 1) Programmer-defined var args // 2) Native bottom types that can accept any argument. // For the first one, "undefined" is a valid value for all arguments. // For the second, we do not want to cast it up to undefined. if (!type.isEmptyType()) { type = registry.createOptionalType(type); } newParameter(type).setVarArgs(true); return true; } /** * Copies the parameter specification from the given node. */ public void newParameterFromNode(Node n) { Node newParam = newParameter(n.getJSType()); newParam.setVarArgs(n.isVarArgs()); newParam.setOptionalArg(n.isOptionalArg()); } // Add a parameter to the list with the given type. private Node newParameter(JSType type) { Node paramNode = Node.newString(Token.NAME, "");

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> this to generate deps files. (We're only using it for // symbol dependencies.) DependencyInfo info = (new JsFileParser(errorManager)).parseFile( getName(), getName(), getCode()); provides.addAll(info.getProvides()); requires.addAll(info.getRequires()); generatedDependencyInfoFromSource = true; } } } private static class DepsFinder { private final List<String> provides = Lists.newArrayList(); private final List<String> requires = Lists.newArrayList(); private final CodingConvention codingConvention = new ClosureCodingConvention(); void visitTree(Node n) { visitSubtree(n, null); } void visitSubtree(Node n, Node parent) { if (n.getType() == Token.CALL) { String require = codingConvention.extractClassNameIfRequire(n, parent); if (require != null) { requires.add(require); } String provide = codingConvention.extractClassNameIfProvide(n, parent); if (provide != null) { provides.add(provide); } return; } else if (parent != null && parent.getType() != Token.EXPR_RESULT && parent.getType() != Token.SCRIPT) { return; } for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { visitSubtree(child, n); } } } /** * Gets the source line for the indicated line number. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Does not include the newline at the end * of the file. Returns {@code null} if it does not exist, * or if there was an IO exception. */ public String getLine(int lineNumber) { return getSourceFile().getLine(lineNumber); } /** * Get a region around the indicated line number. The exact definition of a * region is implementation specific, but it must contain the line indicated * by the line number. A region must not start or end by a carriage return. * * @param lineNumber the line number, 1 being the first line of the file. * @return The line indicated. Returns {@code null} if

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> abstract T processSwitchCase(SwitchCase caseNode); abstract T processSwitchStatement(SwitchStatement statementNode); abstract T processThrowStatement(ThrowStatement statementNode); abstract T processTryStatement(TryStatement statementNode); abstract T processUnaryExpression(UnaryExpression exprNode); abstract T processVariableDeclaration(VariableDeclaration declarationNode); abstract T processVariableInitializer(VariableInitializer initializerNode); abstract T processWhileLoop(WhileLoop loopNode); abstract T processWithStatement(WithStatement statementNode); abstract T processIllegalToken(AstNode node); public T process(AstNode node) { switch (node.getType()) { case Token.ADD: case Token.AND: case Token.BITAND: case Token.BITOR: case Token.BITXOR: case Token.COMMA: case Token.DIV: case Token.EQ: case Token.GE: case Token.GT: case Token.IN: case Token.INSTANCEOF: case Token.LE: case Token.LSH: case Token.LT: case Token.MOD: case Token.MUL: case Token.NE: case Token.OR: case Token.RSH: case Token.SHEQ: case Token.SHNE: case Token.SUB: case Token.URSH: return processInfixExpression((InfixExpression) node); case Token.ARRAYLIT: return processArrayLiteral((ArrayLiteral) node); case Token.ASSIGN: case Token.ASSIGN_ADD: case Token.ASSIGN_BITAND: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_DIV: case Token.ASSIGN_LSH: case Token.ASSIGN_MOD: case Token.ASSIGN_MUL: case Token.ASSIGN_RSH: case Token.ASSIGN_SUB: case Token.ASSIGN_URSH: return processAssignment((Assignment) node); case Token.BITNOT: case Token.DEC: case Token.DELPROP: case Token.INC: case Token.NEG: case Token.NOT: case Token.POS: case Token.TYPEOF: case Token.VOID: return processUnaryExpression((UnaryExpression) node); case Token.BLOCK: if (node instanceof Block

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>((PropertyGet) node); case Token.HOOK: return processConditionalExpression((ConditionalExpression) node); case Token.IF: return processIfStatement((IfStatement) node); case Token.LABEL: return processLabel((Label) node); case Token.LP: return processParenthesizedExpression((ParenthesizedExpression) node); case Token.NAME: return processName((Name) node); case Token.NEW: return processNewExpression((NewExpression) node); case Token.NUMBER: return processNumberLiteral((NumberLiteral) node); case Token.OBJECTLIT: return processObjectLiteral((ObjectLiteral) node); case Token.REGEXP: return processRegExpLiteral((RegExpLiteral) node); case Token.RETURN: return processReturnStatement((ReturnStatement) node); case Token.SCRIPT: return processAstRoot((AstRoot) node); case Token.STRING: return processStringLiteral((StringLiteral) node); case Token.SWITCH: return processSwitchStatement((SwitchStatement) node); case Token.THROW: return processThrowStatement((ThrowStatement) node); case Token.TRY: return processTryStatement((TryStatement) node); case Token.VAR: if (node instanceof VariableDeclaration) { return processVariableDeclaration((VariableDeclaration) node); } else if (node instanceof VariableInitializer) { return processVariableInitializer((VariableInitializer) node); } else { throw new IllegalStateException("Unexpected node type. class: " + node.getClass() + " type: " + Token.typeToName(node.getType())); } case Token.WHILE: return processWhileLoop((WhileLoop) node); case Token.WITH: return processWithStatement((WithStatement) node); } return processIllegalToken(node); } }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>/* * Copyright 2004 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.javascript.jscomp; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Token; import com.google.javascript.rhino.TokenStream; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * NodeUtil contains utilities that get properties from the Node object. * * * */ public final class NodeUtil { // TODO(user): Eliminate this class and make all of the static methods // instance methods of com.google.javascript.rhino.Node. /** the set of builtin constructors that don't have side effects. */ private static final Set<String> CONSTRUCTORS_WITHOUT_SIDE_EFFECTS = new HashSet<String>(Arrays.asList( "Array", "Date", "Error", "Object", "RegExp", "XMLHttpRequest")); // Utility class; do not instantiate. private NodeUtil() {} /** * Gets the boolean value of a node that represents a literal. This method * effectively

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> emulates the <code>Boolean()</code> JavaScript cast function. * * @throws IllegalArgumentException If {@code n} is not a literal value */ static boolean getBooleanValue(Node n) { switch (n.getType()) { case Token.STRING: return n.getString().length() > 0; case Token.NUMBER: return n.getDouble() != 0; case Token.NULL: case Token.FALSE: case Token.VOID: return false; case Token.NAME: String name = n.getString(); if ("undefined".equals(name) || "NaN".equals(name)) { // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return false; } else if ("Infinity".equals(name)) { return true; } break; case Token.TRUE: case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: return true; } throw new IllegalArgumentException("Non-literal value: " + n); } /** * Gets the value of a node as a String, or null if it cannot be converted. * When it returns a non-null String, this method effectively emulates the * <code>String()</code> JavaScript cast function. */ static String getStringValue(Node n) { // TODO(user): Convert constant array, object, and regex literals as well. switch (n.getType()) { case Token.NAME: case Token.STRING: return n.getString(); case Token.NUMBER: double value = n.getDouble(); long longValue = (long) value; // Return "1" instead of "1.0" if (longValue == value) { return Long.toString(longValue); } else { return Double.toString(n.getDouble()); } case Token.FALSE: case Token.TRUE: case Token.NULL: return Node.tokenToName(n.getType()); case Token.VOID: return "undefined"; } return null; } /** * Gets the function's name. This method recognizes five forms: * <ul> * <li>{@code function name() ...}</li> * <li>{@code var

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> name = function() ...}</li> * <li>{@code qualified.name = function() ...}</li> * <li>{@code var name2 = function name1() ...}</li> * <li>{@code qualified.name2 = function name1() ...}</li> * </ul> * In two last cases with named function expressions, the second name is * returned (the variable of qualified name). * * @param n a node whose type is {@link Token#FUNCTION} * @return the function's name, or {@code null} if it has no name */ static String getFunctionName(Node n) { Node parent = n.getParent(); String name = n.getFirstChild().getString(); switch (parent.getType()) { case Token.NAME: // var name = function() ... // var name2 = function name1() ... return parent.getString(); case Token.ASSIGN: // qualified.name = function() ... // qualified.name2 = function name1() ... return parent.getFirstChild().getQualifiedName(); default: // function name() ... return name != null && name.length() != 0 ? name : null; } } /** * Returns true if this is an immutable value. */ static boolean isImmutableValue(Node n) { switch (n.getType()) { case Token.STRING: case Token.NUMBER: case Token.NULL: case Token.TRUE: case Token.FALSE: case Token.VOID: return true; case Token.NEG: return isImmutableValue(n.getFirstChild()); case Token.NAME: String name = n.getString(); // We assume here that programs don't change the value of the keyword // undefined to something other than the value undefined. return "undefined".equals(name) || "Infinity".equals(name) || "NaN".equals(name); } return false; } /** * Returns true if this is a literal value. We define a literal value * as any node that evaluates to the same thing regardless of when or * where it is evaluated. So /xyz/ and [3, 5] are literals, but * function() { return a; } is not. */ static boolean isLiteralValue(Node

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> n) { // TODO(nicksantos): Refine this function to catch more literals. switch (n.getType()) { case Token.ARRAYLIT: case Token.OBJECTLIT: case Token.REGEXP: // Return true only if all children are const. for (Node child = n.getFirstChild(); child != null; child = child.getNext()) { if (!isLiteralValue(child)) { return false; } } return true; default: return isImmutableValue(n); } } /** * Determines whether the given value may be assigned to a define. * * @param val The value being assigned. * @param defines The list of names of existing defines. */ static boolean isValidDefineValue(Node val, Set<String> defines) { switch (val.getType()) { case Token.STRING: case Token.NUMBER: case Token.TRUE: case Token.FALSE: return true; // Single operators are valid if the child is valid. case Token.BITAND: case Token.BITNOT: case Token.BITOR: case Token.BITXOR: case Token.NOT: case Token.NEG: return isValidDefineValue(val.getFirstChild(), defines); // Names are valid if and only if they are defines themselves. case Token.NAME: case Token.GETPROP: if (val.isQualifiedName()) { return defines.contains(val.getQualifiedName()); } } return false; } /** * Returns whether this a BLOCK node with no children. * * @param block The node. */ static boolean isEmptyBlock(Node block) { if (block.getType() != Token.BLOCK) { return false; } for (Node n = block.getFirstChild(); n != null; n = n.getNext()) { if (n.getType() != Token.EMPTY) { return false; } } return true; } /** * A "simple" operator is one whose children are expressions, * has no direct side-effects (unlike '+='), and has no * conditional aspects (unlike '||'). */ static boolean isSimpleOperatorType(int type) { switch (type) { case Token.

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> change state. Otherwise, we assume * that they have no side effects. */ private static boolean checkForStateChangeHelper( Node n, boolean checkForNewObjects, AbstractCompiler compiler) { // Rather than id which ops may have side effects, id the ones // that we know to be safe switch (n.getType()) { // other side-effect free statements and expressions case Token.AND: case Token.BLOCK: case Token.EXPR_RESULT: case Token.HOOK: case Token.IF: case Token.IN: case Token.LP: case Token.NUMBER: case Token.OR: case Token.THIS: case Token.TRUE: case Token.FALSE: case Token.NULL: case Token.STRING: case Token.SWITCH: case Token.TRY: case Token.EMPTY: break; // Throws are by definition side effects case Token.THROW: return true; case Token.OBJECTLIT: case Token.ARRAYLIT: case Token.REGEXP: if (checkForNewObjects) { return true; } break; case Token.VAR: // empty var statement (no declaration) case Token.NAME: // variable by itself if (n.getFirstChild() != null) return true; break; case Token.FUNCTION: // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. return !isFunctionExpression(n); case Token.NEW: if (checkForNewObjects) { return true; } if (!constructorCallHasSideEffects(n)) { // loop below will see if the constructor parameters have // side-effects break; } return true; case Token.CALL: // calls to functions that have no side effects have the no // side effect property set. if (!functionCallHasSideEffects(n, compiler)) { // loop below will see if the function parameters have // side-effects break; } return true; default: if (isSimpleOperatorType(n.getType())) break; if (isAssignmentOp(n)) { // Assignments will have side effects if

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. if (checkForStateChangeHelper( n.getFirstChild(), checkForNewObjects, compiler) || checkForStateChangeHelper( n.getLastChild(), checkForNewObjects, compiler)) { return true; } Node current = n.getFirstChild(); for (; current.getType() == Token.GETPROP || current.getType() == Token.GETELEM; current = current.getFirstChild()) { } return !(isLiteralValue(current) || current.getType() == Token.FUNCTION); } return true; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) { return true; } } return false; } /** * Do calls to this constructor have side effects? * * @param callNode - construtor call node */ static boolean constructorCallHasSideEffects(Node callNode) { Preconditions.checkArgument( callNode.getType() == Token.NEW, "Expected NEW node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); if (nameNode.getType() == Token.NAME && CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) { return false; } return true; } private static final Set<String> REGEXP_METHODS = ImmutableSet.of("test", "exec"); private static final Set<String> STRING_REGEXP_METHODS = ImmutableSet.of("match", "replace", "search", "split"); /** * Returns true if calls to this function have side effects. * * @param callNode - function call node */ static boolean functionCallHasSideEffects( Node callNode) { return functionCallHasSideEffects(callNode, null); } /** * Returns true if calls to this function have side effects. * * @param callNode The

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> call node to inspected. * @param compiler A compiler object to provide program state changing * context information. Can be null. */ static boolean functionCallHasSideEffects( Node callNode, @Nullable AbstractCompiler compiler) { Preconditions.checkArgument( callNode.getType() == Token.CALL, "Expected CALL node, got " + Token.name(callNode.getType())); if (callNode.isNoSideEffectsCall()) { return false; } Node nameNode = callNode.getFirstChild(); // Built-in functions with no side effects. if (nameNode.getType() == Token.NAME) { String name = nameNode.getString(); if (name.equals("String")) { return false; } } else if (nameNode.getType() == Token.GETPROP) { // Functions in the "Math" namespace have no side effects. if (nameNode.getFirstChild().getType() == Token.NAME) { String namespaceName = nameNode.getFirstChild().getString(); if (namespaceName.equals("Math")) { return false; } } if (compiler != null && !compiler.hasRegExpGlobalReferences()) { if (nameNode.getFirstChild().getType() == Token.REGEXP && REGEXP_METHODS.contains(nameNode.getLastChild().getString())) { return false; } else if (nameNode.getFirstChild().getType() == Token.STRING && STRING_REGEXP_METHODS.contains( nameNode.getLastChild().getString())) { Node param = nameNode.getNext(); if (param != null && (param.getType() == Token.STRING || param.getType() == Token.REGEXP)) return false; } } } return true; } /** * Returns true if the current node's type implies side effects. * * This is a non-recursive version of the may have side effects * check; used to check wherever the current node's type is one of * the reason's why a subtree has side effects. */ static boolean nodeTypeMayHaveSideEffects(Node n) { if (isAssignmentOp(n)) { return true; } switch(n.getType()) { case Token.CALL: case Token.DELPROP

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>: case Token.NEW: case Token.DEC: case Token.INC: case Token.THROW: return true; case Token.NAME: // A variable definition. return n.hasChildren(); default: return false; } } /** * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n) { Set<String> emptySet = Collections.emptySet(); return canBeSideEffected(n, emptySet); } /** * @param knownConstants A set of names known to be constant value at * node 'n' (such as locals that are last written before n can execute). * @return Whether the tree can be affected by side-effects or * has side-effects. */ static boolean canBeSideEffected(Node n, Set<String> knownConstants) { switch (n.getType()) { case Token.CALL: case Token.NEW: // Function calls or constructor can reference changed values. // TODO(johnlenz): Add some mechanism for determining that functions // are unaffected by side effects. return true; case Token.NAME: // Non-constant names values may have been changed. return !isConstantName(n) && !knownConstants.contains(n.getString()); // Properties on constant NAMEs can still be side-effected. case Token.GETPROP: case Token.GETELEM: return true; case Token.FUNCTION: // Function expression are not changed by side-effects, // and function declarations are not part of expressions. Preconditions.checkState(isFunctionExpression(n)); return false; } for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (canBeSideEffected(c, knownConstants)) { return true; } } return false; } /* * 0 comma , * 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |= * 2 conditional ?: * 3 logical-or || * 4 logical-and && * 5 bitwise-or | * 6 bitwise-xor ^ * 7 bitwise-

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>: case Token.FALSE: case Token.FUNCTION: case Token.GETELEM: case Token.GETPROP: case Token.GET_REF: case Token.IF: case Token.LP: case Token.NAME: case Token.NULL: case Token.NUMBER: case Token.OBJECTLIT: case Token.REGEXP: case Token.RETURN: case Token.STRING: case Token.THIS: case Token.TRUE: return 15; default: throw new Error("Unknown precedence for " + Node.tokenToName(type) + " (type " + type + ")"); } } /** * Returns true if the operator is associative. * e.g. (a * b) * c = a * (b * c) * Note: "+" is not associative because it is also the concatentation * for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2 */ static boolean isAssociative(int type) { switch (type) { case Token.MUL: case Token.AND: case Token.OR: case Token.BITOR: case Token.BITAND: return true; default: return false; } } static boolean isAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN: case Token.ASSIGN_BITOR: case Token.ASSIGN_BITXOR: case Token.ASSIGN_BITAND: case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: return true; } return false; } static int getOpFromAssignmentOp(Node n) { switch (n.getType()){ case Token.ASSIGN_BITOR: return Token.BITOR; case Token.ASSIGN_BITXOR: return Token.BITXOR; case Token.ASSIGN_BITAND: return Token.BITAND; case Token.ASSIGN_LSH: return Token.LSH; case Token

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.ASSIGN_RSH: return Token.RSH; case Token.ASSIGN_URSH: return Token.URSH; case Token.ASSIGN_ADD: return Token.ADD; case Token.ASSIGN_SUB: return Token.SUB; case Token.ASSIGN_MUL: return Token.MUL; case Token.ASSIGN_DIV: return Token.DIV; case Token.ASSIGN_MOD: return Token.MOD; } throw new IllegalArgumentException("Not an assiment op"); } static boolean isExpressionNode(Node n) { return n.getType() == Token.EXPR_RESULT; } /** * Determines if the given node contains a function declaration. */ static boolean containsFunctionDeclaration(Node n) { return containsType(n, Token.FUNCTION); } /** * Returns true if the subtree contains references to 'this' keyword */ static boolean referencesThis(Node n) { return containsType(n, Token.THIS); } /** * Is this a GETPROP or GETELEM node? */ static boolean isGet(Node n) { return n.getType() == Token.GETPROP || n.getType() == Token.GETELEM; } /** * Is this a GETPROP node? */ static boolean isGetProp(Node n) { return n.getType() == Token.GETPROP; } /** * Is this a NAME node? */ static boolean isName(Node n) { return n.getType() == Token.NAME; } /** * Is this a NEW node? */ static boolean isNew(Node n) { return n.getType() == Token.NEW; } /** * Is this a VAR node? */ static boolean isVar(Node n) { return n.getType() == Token.VAR; } /** * Is this node the name of a variable being declared? * * @param n The node * @return True if {@code n} is NAME and {@code parent} is VAR */ static boolean isVarDeclaration(Node n) { // There is no need to verify that parent != null because a NAME node // always has a parent in a valid parse tree. return n.getType() == Token.NAME

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> n) { switch (n.getType()) { case Token.IF: case Token.WHILE: return n.getFirstChild(); case Token.DO: return n.getLastChild(); case Token.FOR: switch (n.getChildCount()) { case 3: return null; case 4: return n.getFirstChild().getNext(); } throw new IllegalArgumentException("malformed 'for' statement " + n); case Token.CASE: return null; } throw new IllegalArgumentException(n + " does not have a condition."); } /** * @return Whether the node is of a type that contain other statements. */ static boolean isStatementBlock(Node n) { return n.getType() == Token.SCRIPT || n.getType() == Token.BLOCK; } /** * @return Whether the node is used as a statement. */ static boolean isStatement(Node n) { Node parent = n.getParent(); // It is not possible to determine definitely if a node is a statement // or not if it is not part of the AST. A FUNCTION node can be // either part of an expression or a statement. Preconditions.checkState(parent != null); switch (parent.getType()) { case Token.SCRIPT: case Token.BLOCK: case Token.LABEL: return true; default: return false; } } /** Whether the node is part of a switch statement. */ static boolean isSwitchCase(Node n) { return n.getType() == Token.CASE || n.getType() == Token.DEFAULT; } /** * @return Whether the name is a reference to a variable, function or * function parameter (not a label or a empty function expression name). */ static boolean isReferenceName(Node n) { return isName(n) && !n.getString().isEmpty(); } /** @return Whether the node is a label name. */ static boolean isLabelName(Node n) { return (n != null && n.getType() == Token.LABEL_NAME); } /** Whether the child node is the FINALLY block of a try. */ static boolean isTryFinallyNode(Node parent, Node child) { return parent.getType() == Token.TRY && parent.

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>getChildCount() == 3 && child == parent.getLastChild(); } /** Safely remove children while maintaining a valid node structure. */ static void removeChild(Node parent, Node node) { // Node parent = node.getParent(); if (isStatementBlock(parent) || isSwitchCase(node) || isTryFinallyNode(parent, node)) { // A statement in a block can simply be removed. parent.removeChild(node); } else if (parent.getType() == Token.VAR) { if (parent.hasMoreThanOneChild()) { parent.removeChild(node); } else { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // This would leave an empty VAR, remove the VAR itself. removeChild(parent.getParent(), parent); } } else if (node.getType() == Token.BLOCK) { // Simply empty the block. This maintains source location and // "synthetic"-ness. node.detachChildren(); } else if (parent.getType() == Token.LABEL && node == parent.getLastChild()) { // Remove the node from the parent, so it can be reused. parent.removeChild(node); // A LABEL without children can not be referred to, remove it. removeChild(parent.getParent(), parent); } else if (parent.getType() == Token.FOR && parent.getChildCount() == 4) { // Only Token.FOR can have an Token.EMPTY other control structure // need something for the condition. Others need to be replaced // or the structure removed. parent.replaceChild(node, new Node(Token.EMPTY)); } else { throw new IllegalStateException("Invalid attempt to remove node: " + node.toString() + " of "+ parent.toString()); } } /** * Merge a block with its parent block. * @return Whether the block was removed. */ static boolean tryMergeBlock(Node block) { Preconditions.checkState(block.getType() == Token.BLOCK); Node parent = block.getParent(); // Try to remove the block if its parent is a block/script or if its // parent is label and it has exactly one child. if (isStatementBlock(parent)) { Node

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> previous = block; while (block.hasChildren()) { Node child = block.removeFirstChild(); parent.addChildAfter(child, previous); previous = child; } parent.removeChild(block); return true; } else if (parent.getType() == Token.LABEL && block.hasOneChild()) { parent.replaceChild(block, block.removeFirstChild()); return true; } else { return false; } } /** * Is this a CALL node? */ static boolean isCall(Node n) { return n.getType() == Token.CALL; } /** * Is this a FUNCTION node? */ static boolean isFunction(Node n) { return n.getType() == Token.FUNCTION; } /** * Return a BLOCK node for the given FUNCTION node. */ static Node getFunctionBody(Node fn) { Preconditions.checkArgument(isFunction(fn)); return fn.getLastChild(); } /** * Is this a THIS node? */ static boolean isThis(Node node) { return node.getType() == Token.THIS; } /** * Is this node or any of its children a CALL? */ static boolean containsCall(Node n) { return containsType(n, Token.CALL); } /** * Is this node a function declaration? A function declaration is a function * that has a name that is added to the current scope (i.e. a function that * is not part of a expression; see {@link #isFunctionExpression}). */ static boolean isFunctionDeclaration(Node n) { return n.getType() == Token.FUNCTION && isStatement(n); } /** * Is this node a hoisted function declaration? A function declaration in the * scope root is hoisted to the top of the scope. * See {@link #isFunctionDeclaration}). */ static boolean isHoistedFunctionDeclaration(Node n) { return isFunctionDeclaration(n) && (n.getParent().getType() == Token.SCRIPT || n.getParent().getParent().getType() == Token.FUNCTION); } /** * Is a FUNCTION node an function expression? An function expression is one * that has either no name or a name that is not added to the current scope. *

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> * <p>Some examples of function expressions: * <pre> * (function () {}) * (function f() {})() * [ function f() {} ] * var f = function f() {}; * for (function f() {};;) {} * </pre> * * <p>Some examples of functions that are <em>not</em> expressions: * <pre> * function f() {} * if (x); else function f() {} * for (;;) { function f() {} } * </pre> * * @param n A node * @return Whether n is an function used within an expression. */ static boolean isFunctionExpression(Node n) { return n.getType() == Token.FUNCTION && !isStatement(n); } /** * Determines if a function takes a variable number of arguments by * looking for references to the "arguments" var_args object. */ static boolean isVarArgsFunction(Node function) { Preconditions.checkArgument(isFunction(function)); return isNameReferenced( function.getLastChild(), "arguments", Predicates.<Node>not(new MatchNodeType(Token.FUNCTION))); } /** * @return Whether node is a call to methodName. * a.f(...) * a['f'](...) */ static boolean isObjectCallMethod(Node callNode, String methodName) { if (callNode.getType() == Token.CALL) { Node functionIndentifyingExpression = callNode.getFirstChild(); if (isGet(functionIndentifyingExpression)) { Node last = functionIndentifyingExpression.getLastChild(); if (last != null && last.getType() == Token.STRING) { String propName = last.getString(); return (propName.equals(methodName)); } } } return false; } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) */ static boolean isFunctionObjectCall(Node callNode) { return isObjectCallMethod(callNode, "call"); } /** * @return Whether the callNode represents an expression in the form of: * x.apply(...) * x['apply'](...) */

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> static boolean isFunctionObjectApply(Node callNode) { return isObjectCallMethod(callNode, "apply"); } /** * @return Whether the callNode represents an expression in the form of: * x.call(...) * x['call'](...) * where x is a NAME node. */ static boolean isSimpleFunctionObjectCall(Node callNode) { if (isFunctionObjectCall(callNode)) { if (callNode.getFirstChild().getFirstChild().getType() == Token.NAME) { return true; } } return false; } /** * Determines whether this node is strictly on the left hand side of an assign * or var initialization. Notably, this does not include all L-values, only * statements where the node is used only as an L-value. * * @param n The node * @param parent Parent of the node * @return True if n is the left hand of an assign */ static boolean isLhs(Node n, Node parent) { return (parent.getType() == Token.ASSIGN && parent.getFirstChild() == n) || parent.getType() == Token.VAR; } /** * Determines whether a node represents an object literal key * (e.g. key1 in {key1: value1, key2: value2}). * * @param node A node * @param parent The node's parent */ static boolean isObjectLitKey(Node node, Node parent) { if (node.getType() == Token.STRING && parent.getType() == Token.OBJECTLIT) { int index = 0; for (Node current = parent.getFirstChild(); current != null; current = current.getNext()) { if (current == node) { return index % 2 == 0; } index++; } } return false; } /** * Converts an operator's token value (see {@link Token}) to a string * representation. * * @param operator the operator's token value to convert * @return the string representation or {@code null} if the token value is * not an operator */ static String opToStr(int operator) { switch (operator) { case Token.BITOR: return "|";

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> operator) { String res = opToStr(operator); if (res == null) { throw new Error("Unknown op " + operator + ": " + Token.name(operator)); } return res; } /** * @return true if n or any of its children are of the specified type. * Does not traverse into functions. */ static boolean containsTypeInOuterScope(Node node, int type) { return containsType(node, type, Predicates.<Node>not(new MatchNodeType(Token.FUNCTION))); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type, Predicate<Node> traverseChildrenPred) { return has(node, new MatchNodeType(type), traverseChildrenPred); } /** * @return true if n or any of its children are of the specified type */ static boolean containsType(Node node, int type) { return containsType(node, type, Predicates.<Node>alwaysTrue()); } /** * Given a node tree, finds all the VAR declarations in that tree that are * not in an inner scope. Then adds a new VAR node at the top of the current * scope that redeclares them, if necessary. */ static void redeclareVarsInsideBranch(Node branch) { Collection<Node> vars = getVarsDeclaredInBranch(branch); if (vars.isEmpty()) { return; } Node parent = getAddingRoot(branch); for (Node nameNode : vars) { Node var = new Node( Token.VAR, Node.newString(Token.NAME, nameNode.getString()) .copyInformationFrom(nameNode)) .copyInformationFrom(nameNode); copyNameAnnotations(nameNode, var.getFirstChild()); parent.addChildToFront(var); } } /** * Copy any annotations that follow a named value. * @param source * @param destination */ static void copyNameAnnotations(Node source, Node destination) { if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) { destination.putBooleanProp(Node.IS_CONSTANT_NAME, true); } } /** * Gets a Node at

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> the top of the current scope where we can add new var * declarations as children. */ private static Node getAddingRoot(Node n) { Node addingRoot = null; Node ancestor = n; while (null != (ancestor = ancestor.getParent())) { int type = ancestor.getType(); if (type == Token.SCRIPT) { addingRoot = ancestor; break; } else if (type == Token.FUNCTION) { addingRoot = ancestor.getLastChild(); break; } } // make sure that the adding root looks ok Preconditions.checkState(addingRoot.getType() == Token.BLOCK || addingRoot.getType() == Token.SCRIPT); Preconditions.checkState(addingRoot.getFirstChild() == null || addingRoot.getFirstChild().getType() != Token.SCRIPT); return addingRoot; } /** Creates function name(params_0, ..., params_n) { body }. */ public static Node newFunctionNode(String name, List<Node> params, Node body, int lineno, int charno) { Node parameterParen = new Node(Token.LP, lineno, charno); for (Node param : params) { parameterParen.addChildToBack(param); } Node function = new Node(Token.FUNCTION, lineno, charno); function.addChildrenToBack( Node.newString(Token.NAME, name, lineno, charno)); function.addChildToBack(parameterParen); function.addChildToBack(body); return function; } /** * Creates a node representing a qualified name. * * @param name A qualified name (e.g. "foo" or "foo.bar.baz") * @param lineno The source line offset. * @param charno The source character offset from start of the line. * @return A NAME or GETPROP node */ public static Node newQualifiedNameNode(String name, int lineno, int charno) { int endPos = name.indexOf('.'); if (endPos == -1) { return Node.newString(Token.NAME, name, lineno, charno); } Node node = Node.newString(Token.NAME, name.substring(0, endPos), lineno, charno); int startPos; do {

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>Token.NAME, name); nameNode.copyInformationFrom(basisNode); return nameNode; } /** * Creates a new node representing an *existing* name, copying over the source * location information from the basis node and assigning the given original * name to the node. * * @param name The name for the new NAME node. * @param basisNode The node that represents the name as currently found in * the AST. * @param originalName The original name of the item being represented by the * NAME node. Used for debugging information. * * @return The node created. */ static Node newName(String name, Node basisNode, String originalName) { Node nameNode = newName(name, basisNode); nameNode.putProp(Node.ORIGINALNAME_PROP, originalName); return nameNode; } /** Test if all characters in the string are in the Basic Latin (aka ASCII) * character set - that they have UTF-16 values equal to or below 0x7f. * This check can find which identifiers with Unicode characters need to be * escaped in order to allow resulting files to be processed by non-Unicode * aware UNIX tools and editors. * * * See http://en.wikipedia.org/wiki/Latin_characters_in_Unicode * for more on Basic Latin. * * @param s The string to be checked for ASCII-goodness. * * @return True if all characters in the string are in Basic Latin set. */ static boolean isLatin(String s) { char LARGEST_BASIC_LATIN = 0x7f; int len = s.length(); for (int index = 0; index < len; index++) { char c = s.charAt(index); if (c > LARGEST_BASIC_LATIN) { return false; } } return true; } /** * Determines whether the given name can appear on the right side of * the dot operator. Many properties (like reserved words) cannot. */ static boolean isValidPropertyName(String name) { return TokenStream.isJSIdentifier(name) && !TokenStream.isKeyword(name) && // no Unicode escaped characters - some

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> browsers are less tolerant // of Unicode characters that might be valid according to the // language spec. // Note that by this point, unicode escapes have been converted // to UTF-16 characters, so we're only searching for character // values, not escapes. isLatin(name); } private static class VarCollector implements Visitor { final Map<String, Node> vars = Maps.newLinkedHashMap(); public void visit(Node n) { if (n.getType() == Token.NAME) { Node parent = n.getParent(); if (parent != null && parent.getType() == Token.VAR) { String name = n.getString(); if (!vars.containsKey(name)) { vars.put(name, n); } } } } } /** * Retrieves vars declared in the current node tree, excluding descent scopes. */ public static Collection<Node> getVarsDeclaredInBranch(Node root) { VarCollector collector = new VarCollector(); visitPreOrder( root, collector, Predicates.<Node>not(new MatchNodeType(Token.FUNCTION))); return collector.vars.values(); } /** * @return {@code true} if the node an assignment to a prototype property of * some constructor. */ static boolean isPrototypePropertyDeclaration(Node n) { if (!isExprAssign(n)) { return false; } return isPrototypeProperty(n.getFirstChild().getFirstChild()); } static boolean isPrototypeProperty(Node n) { String lhsString = n.getQualifiedName(); if (lhsString == null) { return false; } int prototypeIdx = lhsString.indexOf(".prototype."); return prototypeIdx != -1; } /** * @return The class name part of a qualified prototype name. */ static Node getPrototypeClassName(Node qName) { Node cur = qName; while (isGetProp(cur)) { if (cur.getLastChild().getString().equals("prototype")) { return cur.getFirstChild(); } else { cur = cur.getFirstChild(); } } return null; } /** * @return The string property name part of a qualified prototype name. */ static String getPrototypePropertyName(Node

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> qName) { String qNameStr = qName.getQualifiedName(); int prototypeIdx = qNameStr.lastIndexOf(".prototype."); int memberIndex = prototypeIdx + ".prototype".length() + 1; return qNameStr.substring(memberIndex); } /** * Create a node for an empty result expression: * "void 0" */ static Node newUndefinedNode(Node srcReferenceNode) { // TODO(johnlenz): Why this instead of the more common "undefined"? Node node = new Node(Token.VOID, Node.newNumber(0)); if (srcReferenceNode != null) { node.copyInformationFromForTree(srcReferenceNode); } return node; } /** * Create a VAR node containing the given name and initial value expression. */ static Node newVarNode(String name, Node value) { Node nodeName = Node.newString(Token.NAME, name); if (value != null) { Preconditions.checkState(value.getNext() == null); nodeName.addChildToBack(value); nodeName.copyInformationFrom(value); } Node var = new Node(Token.VAR, nodeName) .copyInformationFrom(nodeName); return var; } /** * A predicate for matching name nodes with the specified node. */ private static class MatchNameNode implements Predicate<Node>{ final String name; MatchNameNode(String name){ this.name = name; } public boolean apply(Node n) { return n.getType() == Token.NAME && n.getString().equals(name); } } /** * A predicate for matching nodes with the specified type. */ static class MatchNodeType implements Predicate<Node>{ final int type; MatchNodeType(int type){ this.type = type; } public boolean apply(Node n) { return n.getType() == type; } } /** * Whether a Node type is within the node tree. */ static boolean isNodeTypeReferenced(Node node, int type) { return isNodeTypeReferenced(node, type, Predicates.<Node>alwaysTrue()); } /** * Whether a Node type is within the node tree. */ static boolean isNodeTypeReferenced( Node node, int

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> node); } /** * A pre-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPreOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { vistor.visit(node); if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPreOrder(c, vistor, traverseChildrenPred); } } } /** * A post-order traversal, calling Vistor.visit for each child matching * the predicate. */ static void visitPostOrder(Node node, Visitor vistor, Predicate<Node> traverseChildrenPred) { if (traverseChildrenPred.apply(node)) { for (Node c = node.getFirstChild(); c != null; c = c.getNext()) { visitPostOrder(c, vistor, traverseChildrenPred); } } vistor.visit(node); } /** * @return Whether a TRY node has a finally block. */ static boolean hasFinally(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getChildCount() == 3; } /** * @return The BLOCK node containing the CATCH node (if any) * of a TRY. */ static Node getCatchBlock(Node n) { Preconditions.checkArgument(n.getType() == Token.TRY); return n.getFirstChild().getNext(); } /** * @return Whether BLOCK (from a TRY node) contains a CATCH. * @see NodeUtil#getCatchBlock */ static boolean hasCatchHandler(Node n) { Preconditions.checkArgument(n.getType() == Token.BLOCK); return n.hasChildren() && n.getFirstChild().getType() == Token.CATCH; } /** * @param fnNode The function. * @return The Node containing the Function parameters. */ static Node getFnParameters(Node fnNode) { // Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ] Preconditions.checkArgument(fnNode.getType() == Token.FUNCTION); return fnNode.getFirstChild().getNext

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>, null); } void add(String str) { cc.add(str); } private void addIdentifier(String identifier) { cc.addIdentifier(identifierEscape(identifier)); } void add(Node n) { add(n, Context.OTHER); } void add(Node n, Context context) { if (!cc.continueProcessing()) { return; } int type = n.getType(); String opstr = NodeUtil.opToStr(type); int childCount = n.getChildCount(); Node first = n.getFirstChild(); Node last = n.getLastChild(); // Handle all binary operators if (opstr != null && first != last) { Preconditions.checkState(childCount == 2); int p = NodeUtil.precedence(type); addLeftExpr(first, p, context); cc.addOp(opstr, true); // For right-hand-side of operations, only pass context if it's // the IN_FOR_INIT_CLAUSE one. Context rhsContext = getContextForNoInOperator(context); // Handle associativity. // e.g. if the parse tree is a * (b * c), // we can simply generate a * b * c. if (last.getType() == type && NodeUtil.isAssociative(type)) { addExpr(last, p, rhsContext); } else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) { // Assignments are the only right-associative binary operators addExpr(last, p, rhsContext); } else { addExpr(last, p + 1, rhsContext); } return; } cc.startSourceMapping(n); switch (type) { case Token.TRY: { Preconditions.checkState(first.getNext().getType() == Token.BLOCK && first.getNext().getChildCount() <= 1); Preconditions.checkState(childCount >= 2 && childCount <= 3); add("try"); add(first, Context.PRESERVE_BLOCK); // second child contains the catch block, or nothing if there // isn't a catch block Node catchblock = first.getNext().getFirstChild();

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> if (catchblock != null) { add(catchblock); } if (childCount == 3) { add("finally"); add(last, Context.PRESERVE_BLOCK); } break; } case Token.CATCH: Preconditions.checkState(childCount == 3); if (first.getNext().getType() != Token.EMPTY) { throw new Error("Catch conditions not suppored because I think" + " that it may be a netscape only feature."); } add("catch("); add(first); add(")"); add(last, Context.PRESERVE_BLOCK); break; case Token.THROW: Preconditions.checkState(childCount == 1); add("throw"); add(first); // Must have a ';' after a throw statement, otherwise safari can't // parse this. cc.endStatement(true); break; case Token.RETURN: add("return"); if (childCount == 1) { add(first); } else { Preconditions.checkState(childCount == 0); } cc.endStatement(); break; case Token.VAR: if (first != null) { add("var "); addList(first, false, getContextForNoInOperator(context)); } break; case Token.LABEL_NAME: Preconditions.checkState(!n.getString().isEmpty()); addIdentifier(n.getString()); break; case Token.NAME: if (first == null || first.getType() == Token.EMPTY) { addIdentifier(n.getString()); } else { Preconditions.checkState(childCount == 1); addIdentifier(n.getString()); cc.addOp("=", true); if (first.getType() == Token.COMMA) { addExpr(first, NodeUtil.precedence(Token.ASSIGN)); } else { // Add expression, consider nearby code at lowest level of // precedence. addExpr(first, 0, getContextForNoInOperator(context)); } } break; case Token.ARRAYLIT: add("["); addList(first, (int[]) n.getProp(Node.SKIP_INDEXES_PROP)); add("]"); break; case Token

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.LP: add("("); addList(first); add(")"); break; case Token.COMMA: addList(first, false, context); break; case Token.NUMBER: Preconditions.checkState(childCount == 0); cc.addNumber(n.getDouble()); break; case Token.TYPEOF: case Token.VOID: case Token.NOT: case Token.BITNOT: case Token.POS: case Token.NEG: { // All of these unary operators are right-associative Preconditions.checkState(childCount == 1); cc.addOp(NodeUtil.opToStrNoFail(type), false); addExpr(first, NodeUtil.precedence(type)); break; } case Token.HOOK: { Preconditions.checkState(childCount == 3); int p = NodeUtil.precedence(type); addLeftExpr(first, p + 1, context); cc.addOp("?", true); addExpr(first.getNext(), p); cc.addOp(":", true); addExpr(last, p); break; } case Token.REGEXP: if (first.getType() != Token.STRING || last.getType() != Token.STRING) { throw new Error("Expected children to be strings"); } String regexp = regexpEscape(first.getString(), outputCharsetEncoder); // I only use one .add because whitespace matters if (childCount == 2) { add(regexp + last.getString()); } else { Preconditions.checkState(childCount == 1); add(regexp); } break; case Token.GET_REF: add(first); break; case Token.REF_SPECIAL: Preconditions.checkState(childCount == 1); add(first); add("."); add((String) n.getProp(Node.NAME_PROP)); break; case Token.FUNCTION: if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } Preconditions.checkState(childCount == 3); boolean funcNeedsParens = (context == Context.START_OF_EXPR); if (funcNeedsParens) { add

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>("("); } add("function"); add(first); add(first.getNext()); add(last, Context.PRESERVE_BLOCK); cc.endFunction(context == Context.STATEMENT); if (funcNeedsParens) { add(")"); } break; case Token.SCRIPT: case Token.BLOCK: { if (n.getClass() != Node.class) { throw new Error("Unexpected Node subclass."); } boolean stripBlock = n.isSyntheticBlock() || ((context != Context.PRESERVE_BLOCK) && (n.getChildCount() < 2)); if (!stripBlock) { cc.beginBlock(); } for (Node c = first; c != null; c = c.getNext()) { add(c, Context.STATEMENT); // VAR doesn't include ';' since it gets used in expressions if (c.getType() == Token.VAR) { cc.endStatement(); } if (c.getType() == Token.FUNCTION) { cc.maybeLineBreak(); } // Prefer to break lines in between top-level statements // because top level statements are more homogeneous. if (type == Token.SCRIPT) { cc.notePreferredLineBreak(); } } if (!stripBlock) { cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } break; } case Token.FOR: if (childCount == 4) { add("for("); if (first.getType() == Token.VAR) { add(first, Context.IN_FOR_INIT_CLAUSE); } else { addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE); } add(";"); add(first.getNext()); add(";"); add(first.getNext().getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { Preconditions.checkState(childCount == 3); add("for("); add(first); add("in"); add(first.getNext()); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false);

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> } break; case Token.DO: Preconditions.checkState(childCount == 2); add("do"); addNonEmptyExpression(first, Context.OTHER, false); add("while("); add(last); add(")"); cc.endStatement(); break; case Token.WHILE: Preconditions.checkState(childCount == 2); add("while("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.EMPTY: Preconditions.checkState(childCount == 0); break; case Token.GETPROP: { Preconditions.checkState(childCount == 2); Preconditions.checkState(last.getType() == Token.STRING); boolean needsParens = (first.getType() == Token.NUMBER); if (needsParens) { add("("); } addLeftExpr(first, NodeUtil.precedence(type), context); if (needsParens) { add(")"); } add("."); addIdentifier(last.getString()); break; } case Token.GETELEM: Preconditions.checkState(childCount == 2); addLeftExpr(first, NodeUtil.precedence(type), context); add("["); add(first.getNext()); add("]"); break; case Token.WITH: Preconditions.checkState(childCount == 2); add("with("); add(first); add(")"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); break; case Token.INC: case Token.DEC: { Preconditions.checkState(childCount == 1); String o = type == Token.INC ? "++" : "--"; int postProp = n.getIntProp(Node.INCRDECR_PROP, 0); // A non-zero post-prop value indicates a post inc/dec, default of zero // is a pre-inc/dec. if (postProp != 0) { addLeftExpr(first, NodeUtil.precedence(type), context); cc.addOp(o, false); } else { cc.addOp(o

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>, false); add(first); } break; } case Token.CALL: // If the left hand side of the call is a direct reference to eval, // then it must have a DIRECT_EVAL annotation. If it does not, then // that means it was originally an indirect call to eval, and that // indirectness must be preserved. if (first.getType() == Token.NAME && "eval".equals(first.getString()) && !first.getBooleanProp(Node.DIRECT_EVAL)) { add("(0,eval)"); } else { addLeftExpr(first, NodeUtil.precedence(type), context); } add("("); addList(first.getNext()); add(")"); break; case Token.IF: boolean hasElse = childCount == 3; boolean ambiguousElseClause = context == Context.BEFORE_DANGLING_ELSE && !hasElse; if (ambiguousElseClause) { cc.beginBlock(); } add("if("); add(first); add(")"); if (hasElse) { addNonEmptyExpression( first.getNext(), Context.BEFORE_DANGLING_ELSE, false); add("else"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), false); } else { addNonEmptyExpression(first.getNext(), Context.OTHER, false); Preconditions.checkState(childCount == 2); } if (ambiguousElseClause) { cc.endBlock(); } break; case Token.NULL: case Token.THIS: case Token.FALSE: case Token.TRUE: Preconditions.checkState(childCount == 0); add(Node.tokenToName(type)); break; case Token.CONTINUE: Preconditions.checkState(childCount <= 1); add("continue"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.DEBUGGER: Preconditions.checkState(childCount == 0); add("debugger"); cc

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.endStatement(); break; case Token.BREAK: Preconditions.checkState(childCount <= 1); add("break"); if (childCount == 1) { if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(" "); add(first); } cc.endStatement(); break; case Token.EXPR_VOID: case Token.EXPR_RESULT: if (type == Token.EXPR_VOID) { throw new Error("Unexpected EXPR_VOID. Should be EXPR_RESULT."); } Preconditions.checkState(childCount == 1); add(first, Context.START_OF_EXPR); cc.endStatement(); break; case Token.NEW: add("new "); int precedence = NodeUtil.precedence(type); // If the first child contains a CALL, then claim higher precedence // to force parens. Otherwise, when parsed, NEW will bind to the // first viable parens if (NodeUtil.containsCall(first)) { precedence = NodeUtil.precedence(first.getType()) + 1; } addExpr(first, precedence); // '()' is optional when no arguments are present Node next = first.getNext(); if (next != null) { add("("); addList(next); add(")"); } break; case Token.STRING: Preconditions.checkState(childCount == 0); add(jsString(n.getString(), outputCharsetEncoder)); break; case Token.DELPROP: Preconditions.checkState(childCount == 1); add("delete "); add(first); break; case Token.OBJECTLIT: { Preconditions.checkState(childCount % 2 == 0); boolean needsParens = (context == Context.START_OF_EXPR); if (needsParens) { add("("); } add("{"); for (Node c = first; c != null; c = c.getNext().getNext()) { if (c != first) { cc.listSeparator(); } // Object literal property names don't have to be quoted if they are // not JavaScript keywords if (

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>c.getType() == Token.STRING && !TokenStream.isKeyword(c.getString()) && TokenStream.isJSIdentifier(c.getString()) && // do not encode literally any non-literal characters that were // unicode escaped. NodeUtil.isLatin(c.getString())) { add(c.getString()); } else { addExpr(c, 1); } add(":"); addExpr(c.getNext(), 1); } add("}"); if (needsParens) { add(")"); } break; } case Token.SWITCH: add("switch("); add(first); add(")"); cc.beginBlock(); addAllSiblings(first.getNext()); cc.endBlock(context == Context.STATEMENT); break; case Token.CASE: Preconditions.checkState(childCount == 2); add("case "); add(first); addCaseBody(last); break; case Token.DEFAULT: Preconditions.checkState(childCount == 1); add("default"); addCaseBody(first); break; case Token.LABEL: Preconditions.checkState(childCount == 2); if (first.getType() != Token.LABEL_NAME) { throw new Error("Unexpected token type. Should be LABEL_NAME."); } add(first); add(":"); addNonEmptyExpression( last, getContextForNonEmptyExpression(context), true); break; // This node is auto generated in anonymous functions and should just get // ignored for our purposes. case Token.SETNAME: break; default: throw new Error("Unknown type " + type + "\n" + n.toStringTree()); } cc.endSourceMapping(n); } /** * Adds a block or expression, substituting a VOID with an empty statement. * This is used for "for (...);" and "if (...);" type statements. * * @param n The node to print. * @param context The context to determine how the node should be printed. */ private void addNonEmptyExpression( Node n, Context context, boolean allowNonBlockChild) { Node nodeToProcess = n; if (!allowNonBlockChild && n.getType

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>() != Token.BLOCK) { throw new Error("Missing BLOCK child."); } // Strip unneeded blocks, that is blocks with <2 children unless // the CodePrinter specifically wants to keep them. if (n.getType() == Token.BLOCK ) { int count = getNonEmptyChildCount(n); if (count == 0) { if (cc.shouldPreserveExtraBlocks()) { cc.beginBlock(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); } else { cc.endStatement(true); } return; } if (count == 1) { // Hack around a couple of browser bugs: // Safari needs a block around function declarations. // IE6/7 needs a block around DOs. Node firstAndOnlyChild = getFirstNonEmptyChild(n); boolean alwaysWrapInBlock = cc.shouldPreserveExtraBlocks(); if (alwaysWrapInBlock || firstAndOnlyChild.getType() == Token.FUNCTION || firstAndOnlyChild.getType() == Token.DO) { cc.beginBlock(); add(firstAndOnlyChild, Context.STATEMENT); cc.maybeLineBreak(); cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT)); return; } else { // Continue with the only child. nodeToProcess = firstAndOnlyChild; } } } if (nodeToProcess.getType() == Token.EMPTY) { cc.endStatement(true); } else { add(nodeToProcess, context); // VAR doesn't include ';' since it gets used in expressions - so any // VAR in a statement context needs a call to endStatement() here. if (nodeToProcess.getType() == Token.VAR) { cc.endStatement(); } } } /** * Adds a node at the left-hand side of an expression. Unlike * {@link #addExpr(Node,int)}, this preserves information about the context. * * The left side of an expression is special because in the JavaScript * grammar, certain tokens may be parsed differently when they are at * the beginning of a statement. For example, "{}" is parsed as a block, * but

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> "{'x': 'y'}" is parsed as an object literal. */ void addLeftExpr(Node n, int minPrecedence, Context context) { addExpr(n, minPrecedence, context); } void addExpr(Node n, int minPrecedence) { addExpr(n, minPrecedence, Context.OTHER); } private void addExpr(Node n, int minPrecedence, Context context) { if ((NodeUtil.precedence(n.getType()) < minPrecedence) || ((context == Context.IN_FOR_INIT_CLAUSE) && (n.getType() == Token.IN))){ add("("); add(n, clearContextForNoInOperator(context)); add(")"); } else { add(n, context); } } void addList(Node firstInList) { addList(firstInList, true, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument) { addList(firstInList, isArrayOrFunctionArgument, Context.OTHER); } void addList(Node firstInList, boolean isArrayOrFunctionArgument, Context lhsContext) { for (Node n = firstInList; n != null; n = n.getNext()) { boolean isFirst = n == firstInList; if (isFirst) { addLeftExpr(n, isArrayOrFunctionArgument ? 1 : 0, lhsContext); } else { cc.listSeparator(); addExpr(n, isArrayOrFunctionArgument ? 1 : 0); } } } /** * This function adds a comma-separated list as is specified by an ARRAYLIT * node with the associated skipIndexes array. This is a space optimization * since we avoid creating a whole Node object for each empty array literal * slot. * @param firstInList The first in the node list (chained through the next * property). * @param skipIndexes If not null, then the array of skipped entries in the * array. */ void addList(Node firstInList, int[] skipIndexes) { int nextSlot = 0; int nextSkipSlot = 0; for (Node n = firstInList; n != null; n

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> than using the CharsetEncoder. if (c > 0x1f && c <= 0x7f) { sb.append(c); } else { // Other characters can be misinterpreted by some js parsers, // or perhaps mangled by proxies along the way, // so we play it safe and unicode escape them. appendHexJavaScriptRepresentation(sb, c); } } } } sb.append(quote); return sb.toString(); } static String identifierEscape(String s) { // First check if escaping is needed at all -- in most cases it isn't. if (NodeUtil.isLatin(s)) { return s; } // Now going through the string to escape non-latin characters if needed. StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); // Identifiers should always go to Latin1/ ASCII characters because // different browser's rules for valid identifier characters are // crazy. if (c > 0x1F && c < 0x7F) { sb.append(c); } else { appendHexJavaScriptRepresentation(sb, c); } } return sb.toString(); } /** Gets the number of children of this node that are non empty. */ private static int getNonEmptyChildCount(Node n) { int i = 0; for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { i++; } } return i; } /** Gets the first non-empty child of the given node. */ private static Node getFirstNonEmptyChild(Node n) { for (Node c = n.getFirstChild(); c != null; c = c.getNext()) { if (c.getType() != Token.EMPTY) { return c; } } return null; } // Information on the current context. Used for disambiguating special cases. // For example, a "{" could indicate the start of an object literal or a // block, depending on the current context. enum Context { STATEMENT, BE

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> Token.SCRIPT) { requiresLineNumbers = true; } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.SCRIPT) { requiresLineNumbers = false; } else if (requiresLineNumbers) { if (n.getLineno() == -1) { // The tree version of the node is really the best diagnostic // info we have to offer here. compiler.report( t.makeError(n, MISSING_LINE_INFO, n.toStringTree())); } } } }

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>.getType() == Token.GETPROP) { methodName = callName.getLastChild().getString(); } else if (callName.getType() == Token.NAME) { String name = callName.getString(); int dollarIndex = name.lastIndexOf('$'); if (dollarIndex != -1) { methodName = name.substring(dollarIndex + 1); } } if (methodName != null) { if (methodName.equals("inherits")) { return SubclassType.INHERITS; } else if (methodName.equals("mixin")) { return SubclassType.MIXIN; } } return null; } @Override public boolean isSuperClassReference(String propertyName) { return "superClass_".equals(propertyName); } /** * Given a qualified name node, strip "prototype" off the end. * * Examples of this transformation: * a.b.c => a.b.c * a.b.c.prototype => a.b.c */ private Node stripPrototype(Node qualifiedName) { if (qualifiedName.getType() == Token.GETPROP && qualifiedName.getLastChild().getString().equals("prototype")) { return qualifiedName.getFirstChild(); } return qualifiedName; } /** * Exctracts X from goog.provide('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfProvide(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.provide"); } /** * Exctracts X from goog.require('X'), if the applied Node is goog. * * @return The extracted class name, or null. */ @Override public String extractClassNameIfRequire(Node node, Node parent){ return extractClassNameIfGoog(node, parent, "goog.require"); } private static String extractClassNameIfGoog(Node node, Node parent, String functionName){ String className = null; if (NodeUtil.isExprCall(parent)) { Node callee = node.getFirstChild(); if (callee != null && callee.getType() == Token.GETPROP) { String qualifiedName = callee

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>TreeTypeAwareEqualsImpl(n2); if (res != null) { return res; } } return res; } public static String tokenToName(int token) { switch (token) { case Token.ERROR: return "error"; case Token.EOF: return "eof"; case Token.EOL: return "eol"; case Token.ENTERWITH: return "enterwith"; case Token.LEAVEWITH: return "leavewith"; case Token.RETURN: return "return"; case Token.GOTO: return "goto"; case Token.IFEQ: return "ifeq"; case Token.IFNE: return "ifne"; case Token.SETNAME: return "setname"; case Token.BITOR: return "bitor"; case Token.BITXOR: return "bitxor"; case Token.BITAND: return "bitand"; case Token.EQ: return "eq"; case Token.NE: return "ne"; case Token.LT: return "lt"; case Token.LE: return "le"; case Token.GT: return "gt"; case Token.GE: return "ge"; case Token.LSH: return "lsh"; case Token.RSH: return "rsh"; case Token.URSH: return "ursh"; case Token.ADD: return "add"; case Token.SUB: return "sub"; case Token.MUL: return "mul"; case Token.DIV: return "div"; case Token.MOD: return "mod"; case Token.BITNOT: return "bitnot"; case Token.NEG: return "neg"; case Token.NEW: return "new"; case Token.DELPROP: return "delprop"; case Token.TYPEOF: return "typeof"; case Token.GETPROP: return "getprop"; case Token.SETPROP: return "setprop"; case Token.GETELEM: return "getelem"; case Token.SETELEM: return "setelem"; case Token.CALL: return "call"; case Token.NAME: return "name"; case Token.NUMBER

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>"; case Token.COLON: return "colon"; case Token.OR: return "or"; case Token.AND: return "and"; case Token.INC: return "inc"; case Token.DEC: return "dec"; case Token.DOT: return "dot"; case Token.FUNCTION: return "function"; case Token.EXPORT: return "export"; case Token.IMPORT: return "import"; case Token.IF: return "if"; case Token.ELSE: return "else"; case Token.SWITCH: return "switch"; case Token.CASE: return "case"; case Token.DEFAULT: return "default"; case Token.WHILE: return "while"; case Token.DO: return "do"; case Token.FOR: return "for"; case Token.BREAK: return "break"; case Token.CONTINUE: return "continue"; case Token.VAR: return "var"; case Token.WITH: return "with"; case Token.CATCH: return "catch"; case Token.FINALLY: return "finally"; case Token.RESERVED: return "reserved"; case Token.NOT: return "not"; case Token.VOID: return "void"; case Token.BLOCK: return "block"; case Token.ARRAYLIT: return "arraylit"; case Token.OBJECTLIT: return "objectlit"; case Token.LABEL: return "label"; case Token.TARGET: return "target"; case Token.LOOP: return "loop"; case Token.EXPR_VOID: return "expr_void"; case Token.EXPR_RESULT: return "expr_result"; case Token.JSR: return "jsr"; case Token.SCRIPT: return "script"; case Token.EMPTY: return "empty"; case Token.GET_REF: return "get_ref"; case Token.REF_SPECIAL: return "ref_special"; } return "<unknown="+token+">"; } /** Returns true if this node is equivalent semantically to another */ public boolean isEquivalentTo(Node node) { if (type == Token.ARRAYLIT) { try { int

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> case Token.ASSIGN_LSH: case Token.ASSIGN_RSH: case Token.ASSIGN_URSH: case Token.ENTERWITH: case Token.LEAVEWITH: case Token.RETURN: case Token.GOTO: case Token.IFEQ: case Token.IFNE: case Token.NEW: case Token.DELPROP: case Token.SETNAME: case Token.SETPROP: case Token.SETELEM: case Token.CALL: case Token.THROW: case Token.RETHROW: case Token.SETVAR: case Token.CATCH_SCOPE: case Token.RETURN_RESULT: case Token.SET_REF: case Token.DEL_REF: case Token.REF_CALL: case Token.TRY: case Token.SEMI: case Token.INC: case Token.DEC: case Token.EXPORT: case Token.IMPORT: case Token.IF: case Token.ELSE: case Token.SWITCH: case Token.WHILE: case Token.DO: case Token.FOR: case Token.BREAK: case Token.CONTINUE: case Token.VAR: case Token.CONST: case Token.WITH: case Token.CATCH: case Token.FINALLY: case Token.BLOCK: case Token.LABEL: case Token.TARGET: case Token.LOOP: case Token.JSR: case Token.SETPROP_OP: case Token.SETELEM_OP: case Token.LOCAL_BLOCK: case Token.SET_REF_OP: return true; default: return false; } } /** * This function takes a set of GETPROP nodes and produces a string that is * each property separated by dots. If the node ultimately under the left * sub-tree is not a simple name, this is not a valid qualified name. * * @return a null if this is not a qualified name, or a dot-separated string * of the name and properties. */ public String getQualifiedName() { if (type == Token.NAME) { return getString(); } else if (type == Token.GETPROP) { String left = getFirstChild().getQualifiedName(); if (left == null) { return

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS> null; } return left + "." + getLastChild().getString(); } else if (type == Token.THIS) { return "this"; } else { return null; } } /** * Returns whether a node corresponds to a simple or a qualified name, such as * <code>x</code> or <code>a.b.c</code> or <code>this.a</code>. */ public boolean isQualifiedName() { switch (getType()) { case Token.NAME: case Token.THIS: return true; case Token.GETPROP: return getFirstChild().isQualifiedName(); default: return false; } } /** * Returns whether a node corresponds to a simple or a qualified name without * a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code> * . */ public boolean isUnscopedQualifiedName() { switch (getType()) { case Token.NAME: return true; case Token.GETPROP: return getFirstChild().isUnscopedQualifiedName(); default: return false; } } // ========================================================================== // Mutators /** * Removes this node from its parent. Equivalent to: * node.getParent().removeChild(); */ public Node detachFromParent() { Preconditions.checkState(parent != null); parent.removeChild(this); return this; } /** * Removes the first child of Node. Equivalent to: * node.removeChild(node.getFirstChild()); * * @return The removed Node. */ public Node removeFirstChild() { Node child = first; if (child != null) { removeChild(child); } return child; } /** * @return A Node that is the head of the list of children. */ public Node removeChildren() { Node children = first; for (Node child = first; child != null; child = child.getNext()) { child.parent = null; } first = null; last = null; return children; } /** * Removes all children from this node and isolates the children from each * other. */ public void detachChildren() { for (

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>") public Set<String> getDirectives() { return (Set<String>) getProp(DIRECTIVES); } /** * Sets the warning suppressions on this node. */ public void setSuppressions(Set<String> val) { Preconditions.checkState(type == Token.FUNCTION || type == Token.SCRIPT); putProp(SUPPRESSIONS, val); } /** * Returns the set of supressions for this node. */ @SuppressWarnings("unchecked") public Set<String> getSuppressions() { return (Set<String>) getProp(SUPPRESSIONS); } /** * Sets whether this is a synthetic block that should not be considered * a real source block. */ public void setWasEmptyNode(boolean val) { putBooleanProp(EMPTY_BLOCK, val); } /** * Returns whether this is a synthetic block that should not be considered * a real source block. */ public boolean wasEmptyNode() { return getBooleanProp(EMPTY_BLOCK); } /** * Marks this function or constructor call node as having no side effects. * This property is only meaningful for {@link Token#CALL} and * {@link Token#NEW} nodes. */ public void setIsNoSideEffectsCall() { Preconditions.checkArgument( getType() == Token.CALL || getType() == Token.NEW, "setIsNoSideEffectsCall only supports CALL and NEW nodes, got " + Token.name(getType())); putBooleanProp(NO_SIDE_EFFECTS_CALL, true); } /** * Returns true if this node is a function or constructor call that * has no side effects. */ public boolean isNoSideEffectsCall() { return getBooleanProp(NO_SIDE_EFFECTS_CALL); } /** * This should only be called for STRING nodes created in object lits. */ public boolean isQuotedString() { return false; } /** * This should only be called for STRING nodes created in object lits. */ public void setQuotedString() { Kit.codeBug(); } static class NodeMismatch { final Node nodeA; final Node nodeB; NodeMismatch(Node nodeA, Node nodeB) { this.nodeA = node

Closure, 100

<FILEB>
<CHANGES>
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN)) {
return false;
}
<CHANGEE>
<CHANGES>
return parent!= null && NodeUtil.isGet(parent);
<CHANGEE>
<FILEE>
<FILEB> } /** * Since this pass reports errors only when a global {@code this} keyword * is encountered, there is no reason to traverse non global contexts. */ public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.FUNCTION) { // Don't traverse functions that are constructors or have the @this // annotation. JSDocInfo jsDoc = getFunctionJsDocInfo(n); if (jsDoc != null && (jsDoc.isConstructor() || jsDoc.hasThisType())) { return false; } // Don't traverse functions unless they would normally // be able to have a @this annotation associated with them. e.g., // var a = function() { }; // or // function a() {} // or // a.x = function() {}; <CHANGES> <CHANGEE> } if (parent != null && parent.getType() == Token.ASSIGN) { Node lhs = parent.getFirstChild(); Node rhs = lhs.getNext(); if (n == lhs) { // Always traverse the left side of the assignment. To handle // nested assignments properly (e.g., (a = this).property = c;), // assignLhsChild should not be overridden. if (assignLhsChild == null) { assignLhsChild = lhs; } } else { } } return true; } public void visit(NodeTraversal t, Node n, Node parent) { if (n.getType() == Token.THIS && shouldReportThis(n, parent)) { compiler.report(t.makeError(n, level, GLOBAL_THIS)); } if (n == assignLhsChild) { assignLhsChild = null; } } private boolean shouldReportThis(Node n, Node parent) { if (assignLhsChild != null) { // Always report a THIS on the left side of an assign. return true; } // Also report a THIS with a property access. <CHANGES> return false; <CHANGEE> } /** * Gets a function's JSDoc information, if it has any. Checks for a few * patterns (ellipses show where JSDoc would be): * <pre> * ... function() {} *<SCANS>ObjectType type : implementedInterfaces) { registry.registerTypeImplementingInterface(this, type); } this.implementedInterfaces = ImmutableList.copyOf(implementedInterfaces); } @Override public boolean hasProperty(String name) { return super.hasProperty(name) || "prototype".equals(name); } @Override public boolean hasOwnProperty(String name) { return super.hasOwnProperty(name) || "prototype".equals(name); } @Override public JSType getPropertyType(String name) { if ("prototype".equals(name)) { return getPrototype(); } else { if (!hasOwnProperty(name)) { if ("call".equals(name)) { // Define the "call" function lazily. Node params = getParametersNode(); if (params == null) { // If there's no params array, don't do any type-checking // in this CALL function. defineDeclaredProperty(name, new FunctionBuilder(registry) .withReturnType(getReturnType()) .build(), false); } else { params = params.cloneTree(); Node thisTypeNode = Node.newString(Token.NAME, "thisType"); thisTypeNode.setJSType( registry.createOptionalNullableType(getTypeOfThis())); params.addChildToFront(thisTypeNode); thisTypeNode.setOptionalArg(true); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParamsNode(params) .withReturnType(getReturnType()) .build(), false); } } else if ("apply".equals(name)) { // Define the "apply" function lazily. FunctionParamBuilder builder = new FunctionParamBuilder(registry); // Ecma-262 says that apply's second argument must be an Array // or an arguments object. We don't model the arguments object, // so let's just be forgiving for now. // TODO(nicksantos): Model the Arguments object. builder.addOptionalParams( registry.createNullableType(getTypeOfThis()), registry.createNullableType( registry.getNativeType(JSTypeNative.OBJECT_TYPE))); defineDeclaredProperty(name, new FunctionBuilder(registry) .withParams(builder) .withReturnType(getReturnType